Ticket #2358: fast-select.diff

File fast-select.diff, 19.5 KB (added by Josh, 10 years ago)

Second version, ignore all the whitespace stripping. (vim did that automatically) See helpers/Selection.cpp for the main changes. This version adds the ability to specify the selection range from JS.

  • binaries/data/mods/public/gui/session/input.js

     
    3636var mouseX = 0;
    3737var mouseY = 0;
    3838var mouseIsOverObject = false;
     39var SELECTION_SEARCH_RANGE = 20;
    3940
    4041// Number of pixels the mouse can move before the action is considered a drag
    4142var maxDragDelta = 4;
     
    495496    var targets = [];
    496497    var target = undefined;
    497498    if (!fromMinimap)
    498         targets = Engine.PickEntitiesAtPoint(x, y);
     499        targets = Engine.PickEntitiesAtPoint(x, y, SELECTION_SEARCH_RANGE);
    499500
    500501    if (targets.length)
    501502        target = targets[0];
     
    11081109        {
    11091110        case "mousemotion":
    11101111            // 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);
    11121113            if (ents.length)
    11131114                g_Selection.setHighlightList([ents[0]]);
    11141115            else
     
    11621163        {
    11631164        case "mousemotion":
    11641165            // 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);
    11661167            if (ents.length)
    11671168                g_Selection.setHighlightList([ents[0]]);
    11681169            else
     
    12121213                return false;
    12131214            }
    12141215
    1215             var ents = Engine.PickEntitiesAtPoint(ev.x, ev.y);
     1216            var ents = Engine.PickEntitiesAtPoint(ev.x, ev.y, SELECTION_SEARCH_RANGE);
    12161217            g_Selection.setHighlightList(ents);
    12171218            return false;
    12181219
    12191220        case "mousebuttonup":
    12201221            if (ev.button == SDL_BUTTON_LEFT)
    12211222            {
    1222                 var ents = Engine.PickEntitiesAtPoint(ev.x, ev.y);
     1223                var ents = Engine.PickEntitiesAtPoint(ev.x, ev.y, SELECTION_SEARCH_RANGE);
    12231224                if (!ents.length)
    12241225                {
    12251226                    if (!Engine.HotkeyIsPressed("selection.add") && !Engine.HotkeyIsPressed("selection.remove"))
  • source/gui/scripting/ScriptFunctions.cpp

     
    148148    cmpCommandQueue->PostNetworkCommand(cmd2);
    149149}
    150150
    151 std::vector<entity_id_t> PickEntitiesAtPoint(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), int x, int y)
     151std::vector<entity_id_t> PickEntitiesAtPoint(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), int x, int y, int range)
    152152{
    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);
    154154}
    155155
    156156std::vector<entity_id_t> PickFriendlyEntitiesInRect(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), int x0, int y0, int x1, int y1, int player)
     
    804804    scriptInterface.RegisterFunction<void, CScriptVal, &PostNetworkCommand>("PostNetworkCommand");
    805805
    806806    // 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");
    808808    scriptInterface.RegisterFunction<std::vector<entity_id_t>, int, int, int, int, int, &PickFriendlyEntitiesInRect>("PickFriendlyEntitiesInRect");
    809809    scriptInterface.RegisterFunction<std::vector<entity_id_t>, int, &PickFriendlyEntitiesOnScreen>("PickFriendlyEntitiesOnScreen");
    810810    scriptInterface.RegisterFunction<std::vector<entity_id_t>, std::string, bool, bool, bool, &PickSimilarFriendlyEntities>("PickSimilarFriendlyEntities");
  • source/simulation2/components/CCmpRangeManager.cpp

     
    9797 * Checks whether v is in a parabolic range of (0,0,0)
    9898 * The highest point of the paraboloid is (0,range/2,0)
    9999 * and the circle of distance 'range' around (0,0,0) on height y=0 is part of the paraboloid
    100  * 
     100 *
    101101 * Avoids sqrting and overflowing.
    102102 */
    103 static bool InParabolicRange(CFixedVector3D v, fixed range) 
     103static bool InParabolicRange(CFixedVector3D v, fixed range)
    104104{
    105105    i32 x = v.X.GetInternalValue(); // abs(x) <= 2^31
    106106    i32 z = v.Z.GetInternalValue();
    107107    u64 xx = (u64)FIXED_MUL_I64_I32_I32(x, x); // xx <= 2^62
    108108    u64 zz = (u64)FIXED_MUL_I64_I32_I32(z, z);
    109109    i64 d2 = (xx + zz) >> 1; // d2 <= 2^62 (no overflow)
    110    
     110
    111111    i32 y = v.Y.GetInternalValue();
    112112    i32 c = range.GetInternalValue();
    113113    i32 c_2 = c >> 1;
     
    327327        // will get confused when trying to run from enemies
    328328        m_LosRevealAll.resize(MAX_LOS_PLAYER_ID+2,false);
    329329        m_SharedLosMasks.resize(MAX_LOS_PLAYER_ID+2,0);
    330        
     330
    331331        m_LosCircular = false;
    332332        m_TerrainVerticesPerSide = 0;
    333333
     
    601601            debug_warn(L"inconsistent subdivs");
    602602    }
    603603
     604    SpatialSubdivision * GetSubdivision()
     605    {
     606        return & m_Subdivision;
     607    }
     608
    604609    // Reinitialise subdivisions and LOS data, based on entity data
    605610    void ResetDerivedData(bool skipLosState)
    606611    {
     
    844849            if (!query.enabled)
    845850                continue;
    846851
    847             CmpPtr<ICmpPosition> cmpSourcePosition(query.source);           
     852            CmpPtr<ICmpPosition> cmpSourcePosition(query.source);
    848853            if (!cmpSourcePosition || !cmpSourcePosition->IsInWorld())
    849854                continue;
    850855
     
    858863            removed.clear();
    859864            // Return the 'added' list sorted by distance from the entity
    860865            // (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(),
    862867                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(),
    864869                std::back_inserter(removed));
    865870            if (added.empty() && removed.empty())
    866871                continue;
     
    927932            }
    928933        }
    929934        // Not the entire world, so check a parabolic range, or a regular range
    930         else if (q.parabolic) 
     935        else if (q.parabolic)
    931936        {
    932937            // elevationBonus is part of the 3D position, as the source is really that much heigher
    933938            CmpPtr<ICmpPosition> cmpSourcePosition(q.source);
     
    944949
    945950                if (!TestEntityQuery(q, it->first, it->second))
    946951                    continue;
    947                
     952
    948953                CmpPtr<ICmpPosition> cmpSecondPosition(GetSimContext(), ents[i]);
    949954                if (!cmpSecondPosition || !cmpSecondPosition->IsInWorld())
    950955                    continue;
     
    952957
    953958                // Restrict based on precise distance
    954959                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,
    957962                        q.maxRange))
    958963                    continue;
    959964
     
    968973            }
    969974        }
    970975        // check a regular range (i.e. not the entire world, and not parabolic)
    971         else 
     976        else
    972977        {
    973978            // Get a quick list of entities that are potentially in range
    974979            SpatialQueryArray ents;
    975980            m_Subdivision.GetNear(ents, pos, q.maxRange);
    976            
     981
    977982            for (int i = 0; i < ents.size(); ++i)
    978983            {
    979984                EntityMap<EntityData>::const_iterator it = m_EntityData.find(ents[i]);
     
    10021007    virtual entity_pos_t GetElevationAdaptedRange(CFixedVector3D pos, CFixedVector3D rot, entity_pos_t range, entity_pos_t elevationBonus, entity_pos_t angle)
    10031008    {
    10041009        entity_pos_t r = entity_pos_t::Zero() ;
    1005        
     1010
    10061011        pos.Y += elevationBonus;
    10071012        entity_pos_t orientation = rot.Y;
    10081013
     
    10221027        {
    10231028            r = r + CFixedVector2D(coords[2*i],coords[2*i+1]).Length() / part;
    10241029        }
    1025        
     1030
    10261031        return r;
    1027        
     1032
    10281033    }
    10291034
    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)
    10311036    {
    1032        
     1037
    10331038        // angle = 0 goes in the positive Z direction
    10341039        entity_pos_t precision = entity_pos_t::FromInt((int)TERRAIN_TILE_SIZE)/8;
    10351040
    10361041        std::vector<entity_pos_t> r;
    1037        
    10381042
     1043
    10391044        CmpPtr<ICmpTerrain> cmpTerrain(GetSystemEntity());
    10401045        CmpPtr<ICmpWaterManager> cmpWaterManager(GetSystemEntity());
    10411046        entity_pos_t waterLevel = cmpWaterManager->GetWaterLevel(pos.X,pos.Z);
    10421047        entity_pos_t thisHeight = pos.Y > waterLevel ? pos.Y : waterLevel;
    10431048
    1044         if (cmpTerrain) 
     1049        if (cmpTerrain)
    10451050        {
    10461051            for (int i = 0; i < numberOfSteps; i++)
    10471052            {
     
    10641069                    r.push_back(maxVector.Y);
    10651070                    continue;
    10661071                }
    1067                
     1072
    10681073                // Loop until vectors come close enough
    10691074                while ((maxVector - minVector).CompareLength(precision) > 0)
    10701075                {
     
    10831088                        minVector = newVector;
    10841089                        minDistance = newDistance;
    10851090                    }
    1086                     else 
     1091                    else
    10871092                    {
    10881093                        // new vector is out parabolic range, so this is a new maxVector
    10891094                        maxVector = newVector;
    10901095                        maxDistance = newDistance;
    10911096                    }
    1092                    
     1097
    10931098                }
    10941099                r.push_back(maxVector.X);
    10951100                r.push_back(maxVector.Y);
    1096                
     1101
    10971102            }
    10981103            r.push_back(r[0]);
    1099             r.push_back(r[1]); 
     1104            r.push_back(r[1]);
    11001105
    11011106        }
    11021107        return r;
    11031108
    11041109    }
    1105    
     1110
    11061111    Query ConstructQuery(entity_id_t source,
    11071112        entity_pos_t minRange, entity_pos_t maxRange,
    11081113        const std::vector<int>& owners, int requiredInterface, u8 flagsMask)
     
    11501155            return;
    11511156        static CColor disabledRingColour(1, 0, 0, 1);   // red
    11521157        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
    11541159        static CColor rayColour(1, 1, 0, 0.2f);
    11551160
    11561161        if (m_DebugOverlayDirty)
     
    11731178                    m_DebugOverlayLines.back().m_Color = (q.enabled ? enabledRingColour : disabledRingColour);
    11741179                    SimRender::ConstructCircleOnGround(GetSimContext(), pos.X.ToFloat(), pos.Y.ToFloat(), q.maxRange.ToFloat(), m_DebugOverlayLines.back(), true);
    11751180                }
    1176                 else 
    1177                 { 
     1181                else
     1182                {
    11781183                    // 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();
    11801185                    pos.Y += q.elevationBonus;
    11811186
    11821187                    std::vector<entity_pos_t> coords;
    1183                    
     1188
    11841189                    // Get the outline from cache if possible
    11851190                    if (ParabolicRangesOutlines.find(q.source.GetId()) != ParabolicRangesOutlines.end())
    11861191                    {
    11871192                        EntityParabolicRangeOutline e = ParabolicRangesOutlines[q.source.GetId()];
    1188                         if (e.position == pos && e.range == q.maxRange) 
     1193                        if (e.position == pos && e.range == q.maxRange)
    11891194                        {
    11901195                            // outline is cached correctly, use it
    1191                             coords = e.outline; 
     1196                            coords = e.outline;
    11921197                        }
    11931198                        else
    11941199                        {
    1195                             // outline was cached, but important parameters changed 
     1200                            // outline was cached, but important parameters changed
    11961201                            // (position, elevation, range)
    11971202                            // update it
    11981203                            coords = getParabolicRangeForm(pos,q.maxRange,q.maxRange*2, entity_pos_t::Zero(), entity_pos_t::FromFloat(2.0f*3.14f),70);
     
    12021207                            ParabolicRangesOutlines[q.source.GetId()] = e;
    12031208                        }
    12041209                    }
    1205                     else 
     1210                    else
    12061211                    {
    1207                         // outline wasn't cached (first time you enable the range overlay 
     1212                        // outline wasn't cached (first time you enable the range overlay
    12081213                        // or you created a new entiy)
    12091214                        // cache a new outline
    12101215                        coords = getParabolicRangeForm(pos,q.maxRange,q.maxRange*2, entity_pos_t::Zero(), entity_pos_t::FromFloat(2.0f*3.14f),70);
     
    12151220                        e.outline = coords;
    12161221                        ParabolicRangesOutlines[q.source.GetId()] = e;
    12171222                    }
    1218                    
     1223
    12191224                    CColor thiscolor = q.enabled ? enabledRingColour : disabledRingColour;
    1220                    
    1221                     // draw the outline (piece by piece)   
     1225
     1226                    // draw the outline (piece by piece)
    12221227                    for (size_t i = 3; i < coords.size(); i += 2)
    12231228                    {
    12241229                        std::vector<float> c;
     
    12681273                {
    12691274                    m_DebugOverlayLines.push_back(SOverlayLine());
    12701275                    m_DebugOverlayLines.back().m_Color = subdivColour;
    1271                    
     1276
    12721277                    float xpos = x*divSize + divSize/2;
    12731278                    float zpos = y*divSize + divSize/2;
    12741279                    SimRender::ConstructSquareOnGround(GetSimContext(), xpos, zpos, divSize, divSize, 0.0f,
  • source/simulation2/components/ICmpRangeManager.h

     
    2424#include "simulation2/system/Interface.h"
    2525#include "simulation2/helpers/Position.h"
    2626#include "simulation2/helpers/Player.h"
     27#include "simulation2/helpers/Spatial.h"
    2728
    2829#include "graphics/Terrain.h" // for TERRAIN_TILE_SIZE
    2930
     
    7374    typedef u32 tag_t;
    7475
    7576    /**
     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    /**
    7683     * Set the bounds of the world.
    7784     * Entities should not be outside the bounds (else efficiency will suffer).
    7885     * @param x0,z0,x1,z1 Coordinates of the corners of the world
     
    118125        entity_pos_t minRange, entity_pos_t maxRange, std::vector<int> owners, int requiredInterface, u8 flags) = 0;
    119126
    120127    /**
    121      * Construct an active query of a paraboloic form around the unit. 
     128     * Construct an active query of a paraboloic form around the unit.
    122129     * The query will be disabled by default.
    123130     * @param source the entity around which the range will be computed.
    124131     * @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.
    127134     *      For units on a different elevation, a physical correct paraboloid with height=maxRange/2 above the unit is used to query them
    128135     * @param elevationBonus extra bonus so the source can be placed higher and shoot further
    129136     * @param owners list of player IDs that matching entities may have; -1 matches entities with no owner.
  • source/simulation2/helpers/Selection.cpp

     
    2727#include "simulation2/components/ICmpTemplateManager.h"
    2828#include "simulation2/components/ICmpSelectable.h"
    2929#include "simulation2/components/ICmpVisual.h"
     30#include "simulation2/helpers/Spatial.h"
    3031#include "ps/CLogger.h"
     32#include "ps/Profiler2.h"
    3133
    32 std::vector<entity_id_t> EntitySelection::PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY, player_id_t player, bool allowEditorSelectables)
     34std::vector<entity_id_t> EntitySelection::PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY, player_id_t player, bool allowEditorSelectables, int range)
    3335{
     36    PROFILE2("PickEntitiesAtPoint");
    3437    CVector3D origin, dir;
    3538    camera.BuildCameraRay(screenX, screenY, origin, dir);
    3639
    3740    CmpPtr<ICmpRangeManager> cmpRangeManager(simulation, SYSTEM_ENTITY);
    3841    ENSURE(cmpRangeManager);
    3942
     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 
    4049    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)
    4451    {
    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;
    4755
     56        CEntityHandle handle = cmpSelectable->GetEntityHandle();
     57
    4858        // Check if this entity is only selectable in Atlas
    49         if (!allowEditorSelectables && static_cast<ICmpSelectable*>(it->second)->IsEditorOnly())
     59        if (!allowEditorSelectables && cmpSelectable->IsEditorOnly())
    5060            continue;
    5161
    5262        // Ignore entities hidden by LOS (or otherwise hidden, e.g. when not IsInWorld)
     
    91101        CVector3D closest = origin + dir * (center - origin).Dot(dir);
    92102        dist2 = (closest - center).LengthSquared();
    93103
    94         hits.push_back(std::make_pair(dist2, ent));
     104        hits.push_back(std::make_pair(dist2, ents[i]));
    95105    }
    96106
    97107    // Sort hits by distance
     
    148158        CVector3D position = cmpVisual->GetPosition();
    149159
    150160        // Reject if it's not on-screen (e.g. it's behind the camera)
    151 
    152161        if (!camera.GetFrustum().IsPointVisible(position))
    153162            continue;
    154163
  • source/simulation2/helpers/Selection.h

     
    4343 *  this value is ignored as the whole map is revealed.
    4444 * @param allowEditorSelectables if true, all entities with the ICmpSelectable interface
    4545 *  will be selected (including decorative actors), else only those selectable ingame.
     46 * @param range Approximate range to check for entity in.
    4647 *
    4748 * @return ordered list of selected entities with the closest first.
    4849 */
    49 std::vector<entity_id_t> PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY, player_id_t player, bool allowEditorSelectables);
     50std::vector<entity_id_t> PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY, player_id_t player, bool allowEditorSelectables, int range);
    5051
    5152/**
    5253 * Finds all selectable entities within the given screen coordinate rectangle,
  • source/simulation2/helpers/Spatial.h

     
    1818#ifndef INCLUDED_SPATIAL
    1919#define INCLUDED_SPATIAL
    2020
     21#include "simulation2/system/Component.h"
    2122#include "simulation2/serialization/SerializeTemplates.h"
    2223#include "ps/CLogger.h"
    2324
  • source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp

     
    442442   
    443443    // Normally this function would be called with a player ID to check LOS,
    444444    //  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);
    446446
    447447    // Multiple entities may have been picked, but they are sorted by distance,
    448448    //  so only take the first one