• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Camera.getViewBounds()

Status
Not open for further replies.
Level 24
Joined
Aug 1, 2013
Messages
4,657
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,657
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