• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.
  • Create a faction for Warcraft 3 and enter Hive's 19th Techtree Contest: Co-Op Commanders! Click here to enter!
  • Create a void inspired texture for Warcraft 3 and enter Hive's 34th Texturing Contest: Void! Click here to enter!
  • The Hive's 21st Texturing Contest: Upgrade is now concluded, time to vote for your favourite set of icons! Click here to vote!

Camera.getViewBounds()

Status
Not open for further replies.
Level 24
Joined
Aug 1, 2013
Messages
4,658
As I was doing some stuff with selection of units, I ended up making a function that returns the smallest rect of the current camera view.
Aka, all points that you can see are inside the rect without making the rect needlessly large.

JASS:
struct Camera
  
    private static method screenCoordinateToLocation takes Real2D r returns Real2D
        local real angleOfAttackEx = (GetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK) - 270) * bj_DEGTORAD
        local real resolution = 1366. / 676.
        local real totalHeight
        local real groundOffset
        local real totalDistance
        local real xOffset
        local real rot = GetCameraField(CAMERA_FIELD_ROTATION)
        local real cosRotation = Cos(rot * bj_DEGTORAD)
        local real sinRotation = Sin(rot * bj_DEGTORAD)
        local real oldX
        local real fieldOfViewEx = GetCameraField(CAMERA_FIELD_FIELD_OF_VIEW)/2 * bj_DEGTORAD
        local real zOffset = GetCameraField(CAMERA_FIELD_ZOFFSET)
        local real targetDistance = GetCameraField(CAMERA_FIELD_TARGET_DISTANCE)
        local real roll = GetCameraField(CAMERA_FIELD_ROLL)
     
        call r.setY(r.y/resolution)
        call r.rotate(roll * bj_DEGTORAD)
        set oldX = r.x
        set totalHeight = zOffset + targetDistance*Cos(angleOfAttackEx)
        set groundOffset = Tan(angleOfAttackEx + fieldOfViewEx*r.y) * totalHeight
     
        call r.setX(GetCameraEyePositionX() + groundOffset*cosRotation)
        call r.setY(GetCameraEyePositionY() + groundOffset*sinRotation)
     
        set totalDistance = SquareRoot(totalHeight*totalHeight + groundOffset*groundOffset)
        set xOffset = Tan(fieldOfViewEx*oldX) * totalDistance *0.8
     
        call r.setX(r.x + xOffset*sinRotation)
        call r.setY(r.y + xOffset*cosRotation)
     
        return r
    endmethod
 
    public static method getViewBounds takes real margin returns rect
        local Real2D r1 = .screenCoordinateToLocation(Real2D.create(-1, -1))
        local Real2D r2 = .screenCoordinateToLocation(Real2D.create(1, -1))
        local Real2D r3 = .screenCoordinateToLocation(Real2D.create(-1, 1))
        local Real2D r4 = .screenCoordinateToLocation(Real2D.create(1, 1))
        local real minX = r1.x
        local real maxX = r4.x
        local real minY = r1.y
        local real maxY = r4.y
     
        if r2.x < minX then
            set minX = r2.x
            set maxX = r3.x
        endif
        if r3.x < minX then
            set minX = r3.x
            set maxX = r2.x
        endif
        if r4.x < minX then
            set minX = r4.x
            set maxX = r1.x
        endif
     
        if r2.y < minY then
            set minY = r2.y
            set maxY = r3.y
        endif
        if r3.y < minY then
            set minY = r3.y
            set maxY = r2.y
        endif
        if r4.y < minY then
            set minY = r4.y
            set maxY = r1.y
        endif
     
        call r1.destroy()
        call r2.destroy()
        call r3.destroy()
        call r4.destroy()
        return Rect(minX - margin, minY - margin, maxX + margin, maxY + margin)
    endmethod
  
endstruct

JASS:
struct Real2D
 
    readonly real x
    readonly real y
 
    public static method create takes real x, real y returns thistype
        local thistype this = .allocate()
        set .x = x
        set .y = y
        return this
    endmethod
 
    public static method createEx takes nothing returns thistype
        local thistype this = .allocate()
        return this
    endmethod
 
    public method destroy takes nothing returns nothing
        call .deallocate()
    endmethod
 
    public method setX takes real x returns nothing
        set .x = x
    endmethod
 
    public method setY takes real y returns nothing
        set .y = y
    endmethod
 
    public method rotateAroundCoordinate takes real angle, real pX, real pY returns nothing
        local real oldX = .x
        local real oldY = .y
        local real sin = Sin(angle)
        local real cos = Cos(angle)
        set .x = (pX + ((oldX - pX) * cos) - ((oldY - pY) * sin))
        set .y = (pY + ((oldX - pX) * sin) + ((oldY - pY) * cos))
    endmethod
 
    public method rotate takes real angle returns nothing
        call .rotateAroundCoordinate(angle, 0, 0)
    endmethod
 
    public method toString takes nothing returns string
        return "("+R2S(.x)+", "+R2S(.y)+")"
    endmethod
 
endstruct

Be aware that this does not take terrain height into account.

This also has a slightly larger width than the camera, hence why I set it to 0.8 of the width.

This also assumes that you dont have a bull shit angle of attack... aka, an angle higher than 90 degrees.

Last but not least, it doesnt read in the resolution of the screen, so I have taken a rough estimation of the display resolution.

EDIT:
Dont mind the part where I check what the minX, minY, maxX and maxY are.
I know it doesnt properly work ;)
JASS:
    public method setPointsRandom takes Real2D r1, Real2D r2, Real2D r3, Real2D r4 returns thistype
        set .x = r1.x
        if r2.x < .x then
            set .x = r2.x
        endif
        if r3.x < .x then
            set .x = r3.x
        endif
        if r4.x < .x then
            set .x = r4.x
        endif
      
        set .w = r1.x
        if r2.x > .w then
            set .w = r2.x
        endif
        if r3.x > .w then
            set .w = r3.x
        endif
        if r4.x > .w then
            set .w = r4.x
        endif
        set .w = .w-.x
      
      
        set .y = r1.y
        if r2.y < .y then
            set .y = r2.y
        endif
        if r3.y < .y then
            set .y = r3.y
        endif
        if r4.y < .y then
            set .y = r4.y
        endif
      
        set .h = r1.y
        if r2.y > .h then
            set .h = r2.y
        endif
        if r3.y > .h then
            set .h = r3.y
        endif
        if r4.y > .h then
            set .h = r4.y
        endif
        set .h = .h-.y
      
        return this
    endmethod
 
Last edited:
Level 24
Joined
Aug 1, 2013
Messages
4,658
Well... I used this in a selection snippet.
When you select an enemy unit, your selection will be reset to your old selection and the enemy unit's info will be displayed on a multiboard.

In the end, I was required to pick all enemy units an check if they are selected.
It is stupid though to check for units on the other side of the map.
So I created this to only get the units that would be possibly visible on the screen and thus possibly able to be clicked on.
However, I think I might do it on a much simpler approach because this runs every 0.03 seconds and takes quite a few calculations.
 
Status
Not open for further replies.
Top