• 🏆 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!

Physics Engine Development

Status
Not open for further replies.
Level 18
Joined
Jan 21, 2006
Messages
2,552
Tell me what you guys think.

This is kind of a simple sketch of a Physics engine I was planning. If you're going to use the test-map, it creates 21 particles at initialization (they bounce around terrain) and you can use the ESC key to create a field at the current camera position.

I'm pretty sure that the game will crash if you let any of the particles start wondering outside of the map area (since I do not do any sentinel checks yet), so be sure you make a field that will suck them back in.

I was actually planning on using this primarily for cool looking special effects on spells, but if you guys happen to see any other use for it perhaps I could add some functionality.


JASS:
library Vector

//*****************************************************************
//*  VECTOR LIBRARY
//*
//*  written by: Anitarf
//*
//*  The library contains a struct named vector, which represents a
//*  point in 3D space. As such, it has three real members, one for
//*  each coordinate: x, y, z. It also has the following methods:
//*
//*        static method create takes real x, real y, real z returns vector
//*  Creates a new vector with the given coordinates.
//*
//*        method getLength takes nothing returns real
//*  Returns the length of the vector it is called on.
//*
//*        static method sum takes vector augend, vector addend returns vector
//*  Returns the sum of two vectors as a new vector.
//*
//*        method add takes vector addend returns nothing
//*  Similar to sum, except that it doesn't create a new vector for the result,
//*  but changes the vector it is called on by adding the "added" to it.
//*
//*        static method difference takes vector minuend, vector subtrahend returns vector
//*  Returns the difference between two vectors as a new vector.
//*
//*        method subtract takes vector subtrahend returns nothing
//*  Similar to difference, except that it doesn't create a new vector for the result,
//*  but changes the vector it is called on by subtracting the "subtrahend" from it.
//*
//*        method scale takes real factor returns nothing
//*  Scales the vector it is called on by the given factor.
//*
//*        method setLength takes real length returns nothing
//*  Sets the length of the vector it is called on to the given value, maintaining it's orientation.
//*
//*        static method dotProduct takes vector a, vector b returns real
//*  Calculates the dot product (also called scalar product) of two vectors.
//*
//*        static method crossProduct takes vector a, vector b returns vector
//*  Calculates the cross product (also called vector product) of two vectors
//*  and returns it as a new vector.
//*
//*        static method tripleProductScalar takes vector a, vector b, vector c returns real
//*  Calculates the triple scalar product of three vectors.
//*
//*        static method tripleProductVector takes vector a, vector b, vector c returns vector
//*  Calculates the triple vector product of three vectors and returns it as a new vector.
//*
//*
//*        static method projectionVector takes vector projected, vector direction returns vector
//*  Calculates the projection of the vector "projected" onto the vector "direction"
//*  and returns it as a new vector.
//*  Returns null if the vector direction has a length of 0.
//*
//*        method projectVector takes vector direction returns nothing
//*  Projects the vector it is called on onto the vector "direction".
//*  Does nothing if the vector "direction" has a length of 0.
//*
//*        static method projectionPlane takes vector projected, vector normal returns vector
//*  Calculates the projection of the vector projected onto a plane defined by
//*  it's normal vector and returns it as a new vector.
//*  Returns null if the vector "normal" has a length of 0.
//*
//*        method projectPlane takes vector normal returns nothing
//*  Projects the vector it is called on onto a plane defined by it's normal vector.
//*  Does nothing if the vector "normal" has a length of 0.
//*
//*        static method getAngle takes vector a, vector b returns real
//*  Returns the angle between two vectors, in radians, returns a value between 0 and pi.
//*  Returns 0.0 if any of the vectors are 0 units long.
//*
//*        method rotate takes vector axis, real angle returns nothing
//*  Rotates the vector it is called on around the axis defined by the vector "axis"
//*  by the given angle, which should be input in radians.
//*  Does nothing if axis is 0 units long.
//*
//*
//*        static method createTerrainPoint takes real x, real y returns vector
//*  Creates a vector to the given terrain coordinate, taking it's z height into account.
//*
//*        method getTerrainPoint takes real x, real y returns nothing
//*  Sets the vector it is called on to the given terrain coordinate, taking it's z height into account.
//*
//*        static method createTerrainNormal takes real x, real y, real sampleRadius returns vector
//*  Creates the normal vector of the terrain at given coordinates. "sampleRadius" defines
//*  how far apart the reference points will be, if they are further apart, the result will
//*  be an impression of smoother terrain; normaly the value should be between 0 and 128.
//*
//*        method getTerrainNormal takes real x, real y, real sampleRadius returns nothing
//*  Sets the vector it is called on to the normal of the terrain at given coordinates.
//*
//*
//*        method isInCylinder takes vector cylinderOrigin, vector cylinderHeight, real cylinderRadius returns boolean
//*  Determines if a point is within a given cylinder. The cylinder's origin vector points
//*  to the center of one of the two paralel circular sides, and the height vector points
//*  from the origin point to the center of the other of the two paralel circular sides.
//*  Returns false if the point is not in the cylinder or if the vector cylinderHeight is 0 units long.
//*
//*        method isInCone takes vector coneOrigin, vector coneHeight, real coneRadius returns boolean
//*  Determines if a point is within a given cone. The cone's origin vector points to the
//*  center of the circular side, and the height vector points from the origin point to
//*  the tip of the cone.
//*  Returns false if the point is not in the cylinder or if the vector coneHeight is 0 units long.
//*
//*        method isInSphere takes vector sphereOrigin, real sphereRadius returns boolean
//*  Determines if a point is within a give sphere. The sphere's origin vector points to the
//*  center of the sphere.
//*  Returns false if the point is not in the sphere.
//*****************************************************************

    struct vector
        real x
        real y
        real z
        
        static method create takes real x, real y, real z returns vector
            local vector v = vector.allocate()
            set v.x=x
            set v.y=y
            set v.z=z
            return v
        endmethod
        
        method getLength takes nothing returns real
          return SquareRoot(.x*.x + .y*.y + .z*.z)
        endmethod
        
        method copy takes nothing returns thistype
            local thistype v = allocate()
            set v.x = x
            set v.y = y
            set v.z = z
            return v
        endmethod
        
        static method sum takes vector augend, vector addend returns vector
            local vector v = vector.allocate()
            set v.x = augend.x+addend.x
            set v.y = augend.y+addend.y
            set v.z = augend.z+addend.z
            return v
        endmethod
        method add takes vector addend returns nothing
            set this.x=this.x+addend.x
            set this.y=this.y+addend.y
            set this.z=this.z+addend.z
        endmethod
        
        static method difference takes vector minuend, vector subtrahend returns vector
            local vector v = vector.allocate()
            set v.x = minuend.x-subtrahend.x
            set v.y = minuend.y-subtrahend.y
            set v.z = minuend.z-subtrahend.z
            return v
        endmethod
        method subtract takes vector subtrahend returns nothing
            set this.x=this.x-subtrahend.x
            set this.y=this.y-subtrahend.y
            set this.z=this.z-subtrahend.z
        endmethod
        
        method scale takes real factor returns nothing
            set this.x=this.x*factor
            set this.y=this.y*factor
            set this.z=this.z*factor
        endmethod
        
        method setLength takes real length returns nothing
            local real l = SquareRoot(.x*.x + .y*.y + .z*.z)
            if l == 0.0 then
                debug call BJDebugMsg("Attempted to set the length of a vector with no length!")
                return
            endif
            set l = length/l
            set this.x = this.x*l
            set this.y = this.y*l
            set this.z = this.z*l
        endmethod
        
        static method dotProduct takes vector a, vector b returns real
            return (a.x*b.x+a.y*b.y+a.z*b.z)
        endmethod
        
        static method crossProduct takes vector a, vector b returns vector
            local vector v = vector.allocate()
            set v.x = a.y*b.z - a.z*b.y
            set v.y = a.z*b.x - a.x*b.z
            set v.z = a.x*b.y - a.y*b.x
            return v
        endmethod

        static method tripleProductScalar takes vector a, vector b, vector c returns real
            return ((a.y*b.z - a.z*b.y)*c.x+(a.z*b.x - a.x*b.z)*c.y+(a.x*b.y - a.y*b.x)*c.z)
        endmethod

        static method tripleProductVector takes vector a, vector b, vector c returns vector
            local vector v = vector.allocate()
            local real n = a.x*c.x+a.y*c.y+a.z*c.z
            local real m = a.x*b.x+a.y*b.y+a.z*b.z
            set v.x = b.x*n-c.x*m
            set v.y = b.y*n-c.y*m
            set v.z = b.z*n-c.z*m
            return v
        endmethod

// ================================================================

        static method projectionVector takes vector projected, vector direction returns vector
            local vector v = vector.allocate()
            local real l = direction.x*direction.x+direction.y*direction.y+direction.z*direction.z
            if l == 0.0 then
                call v.destroy()
                debug call BJDebugMsg("Attempted to project onto a vector with no length!")
                return null
            endif
            set l = (projected.x*direction.x+projected.y*direction.y+projected.z*direction.z) / l
            set v.x = direction.x*l
            set v.y = direction.y*l
            set v.z = direction.z*l
            return v
        endmethod
        method projectVector takes vector direction returns nothing
            local real l = direction.x*direction.x+direction.y*direction.y+direction.z*direction.z
            if l == 0.0 then
                debug call BJDebugMsg("Attempted to project onto a vector with no length!")
                return
            endif
            set l = (this.x*direction.x+this.y*direction.y+this.z*direction.z) / l
            set this.x = direction.x*l
            set this.y = direction.y*l
            set this.z = direction.z*l
        endmethod

        static method projectionPlane takes vector projected, vector normal returns vector
            local vector v = vector.allocate()
            local real l = normal.x*normal.x+normal.y*normal.y+normal.z*normal.z
            if l == 0.0 then
                call v.destroy()
                debug call BJDebugMsg("Attempted to project onto an undefined plane!")
                return null
            endif
            set l = (projected.x*normal.x+projected.y*normal.y+projected.z*normal.z) / l
            set v.x = projected.x - normal.x*l
            set v.y = projected.y - normal.y*l
            set v.z = projected.z - normal.z*l
            return v
        endmethod
        method projectPlane takes vector normal returns nothing
            local real l = normal.x*normal.x+normal.y*normal.y+normal.z*normal.z
            if l == 0.0 then
                debug call BJDebugMsg("Attempted to project onto an undefined plane!")
                return
            endif
            set l = (this.x*normal.x+this.y*normal.y+this.z*normal.z) / l
            set this.x = this.x - normal.x*l
            set this.y = this.y - normal.y*l
            set this.z = this.z - normal.z*l
        endmethod

        static method getAngle takes vector a, vector b returns real
            local real l = SquareRoot(a.x*a.x + a.y*a.y + a.z*a.z)*SquareRoot(b.x*b.x + b.y*b.y + b.z*b.z)
            if l == 0 then
                debug call BJDebugMsg("Attempted to get angle between vectors with no length!")
                return 0.0
            endif
            return Acos((a.x*b.x+a.y*b.y+a.z*b.z)/l) //angle is returned in radians
        endmethod
        
        method rotate takes vector axis, real angle returns nothing //angle is taken in radians
            local real xx
            local real xy
            local real xz
            local real yx
            local real yy
            local real yz
            local real zx
            local real zy
            local real zz
            local real al = axis.x*axis.x+axis.y*axis.y+axis.z*axis.z //axis length^2
            local real f
            local real c = Cos(angle)
            local real s = Sin(angle)
            if al == 0.0 then
                debug call BJDebugMsg("Attempted to project onto a vector with no length!")
                return
            endif
            set f = (this.x*axis.x+this.y*axis.y+this.z*axis.z) / al
            set zx = axis.x*f
            set zy = axis.y*f
            set zz = axis.z*f //axis component of rotated vector
            set xx = this.x-zx
            set xy = this.y-zy
            set xz = this.z-zz //component of vector perpendicular to axis
            set al = SquareRoot(al)
            set yx = (axis.y*xz - axis.z*xy)/al
            set yy = (axis.z*xx - axis.x*xz)/al //y same length as x by using cross product and dividing with axis length
            set yz = (axis.x*xy - axis.y*xx)/al //x,y - coordinate system in which we rotate
            set this.x=xx*c+yx*s+zx
            set this.y=xy*c+yy*s+zy
            set this.z=xz*c+yz*s+zz
        endmethod
        
// ================================================================

        private static location loc = Location(0.0,0.0)

        static method createTerrainPoint takes real x, real y returns vector
            local vector v = vector.allocate()
            call MoveLocation(vector.loc,x,y)
            set v.x=x
            set v.y=y
            set v.z=GetLocationZ(loc)
            return v
        endmethod
        method getTerrainPoint takes real x, real y returns nothing
            call MoveLocation(vector.loc,x,y)
            set this.x=x
            set this.y=y
            set this.z=GetLocationZ(loc)
        endmethod

        static method createTerrainNormal takes real x, real y, real sampleRadius returns vector
            local vector v = vector.allocate()
            local real zx
            local real zy
            call MoveLocation(vector.loc, x-sampleRadius, y)
            set zx=GetLocationZ(vector.loc)
            call MoveLocation(vector.loc, x+sampleRadius, y)
            set zx=zx-GetLocationZ(vector.loc)
            call MoveLocation(vector.loc, x, y-sampleRadius)
            set zy=GetLocationZ(vector.loc)
            call MoveLocation(vector.loc, x, y+sampleRadius)
            set zy=zy-GetLocationZ(vector.loc)
            set sampleRadius=2*sampleRadius
            set v.x = zx*sampleRadius
            set v.y = zy*sampleRadius
            set v.z = sampleRadius*sampleRadius
            return v
        endmethod
        method getTerrainNormal takes real x, real y, real sampleRadius returns nothing
            local real zx
            local real zy
            call MoveLocation(vector.loc, x-sampleRadius, y)
            set zx=GetLocationZ(vector.loc)
            call MoveLocation(vector.loc, x+sampleRadius, y)
            set zx=zx-GetLocationZ(vector.loc)
            call MoveLocation(vector.loc, x, y-sampleRadius)
            set zy=GetLocationZ(vector.loc)
            call MoveLocation(vector.loc, x, y+sampleRadius)
            set zy=zy-GetLocationZ(vector.loc)
            set sampleRadius=2*sampleRadius
            set this.x = zx*sampleRadius
            set this.y = zy*sampleRadius
            set this.z = sampleRadius*sampleRadius
        endmethod

// ================================================================

        method isInCylinder takes vector cylinderOrigin, vector cylinderHeight, real cylinderRadius returns boolean
            local real l

            local real x = this.x-cylinderOrigin.x
            local real y = this.y-cylinderOrigin.y
            local real z = this.z-cylinderOrigin.z
            if x*cylinderHeight.x+y*cylinderHeight.y+z*cylinderHeight.z < 0.0 then //point below cylinder
                return false
            endif
            
            set x = x-cylinderHeight.x
            set y = y-cylinderHeight.y
            set z = z-cylinderHeight.z
            if x*cylinderHeight.x+y*cylinderHeight.y+z*cylinderHeight.z > 0.0 then //point above cylinder
                return false
            endif
            
            set l = cylinderHeight.x*cylinderHeight.x+cylinderHeight.y*cylinderHeight.y+cylinderHeight.z*cylinderHeight.z
            if l == 0.0 then
                debug call BJDebugMsg("Cylinder with no height!")
                return false
            endif
            set l = (x*cylinderHeight.x+y*cylinderHeight.y+z*cylinderHeight.z) / l
            set x = x - cylinderHeight.x*l
            set y = y - cylinderHeight.y*l
            set z = z - cylinderHeight.z*l
            if x*x+y*y+z*z > cylinderRadius*cylinderRadius then //point outside cylinder
                return false
            endif
            
            return true
        endmethod

        method isInCone takes vector coneOrigin, vector coneHeight, real coneRadius returns boolean
            local real l

            local real x = this.x-coneOrigin.x
            local real y = this.y-coneOrigin.y
            local real z = this.z-coneOrigin.z
            if x*coneHeight.x+y*coneHeight.y+z*coneHeight.z < 0.0 then //point below cone
                return false
            endif
            
            set l = coneHeight.x*coneHeight.x+coneHeight.y*coneHeight.y+coneHeight.z*coneHeight.z
            if l == 0.0 then
                debug call BJDebugMsg("cone with no height!")
                return false
            endif
            set l = (x*coneHeight.x+y*coneHeight.y+z*coneHeight.z) / l
            set x = x - coneHeight.x*l
            set y = y - coneHeight.y*l
            set z = z - coneHeight.z*l
            if SquareRoot(x*x+y*y+z*z) > coneRadius*(1.0-l) then //point outside cone
                return false
            endif
            
            return true
        endmethod

        method isInSphere takes vector sphereOrigin, real sphereRadius returns boolean
            if sphereRadius*sphereRadius < ((this.x-sphereOrigin.x)*(this.x-sphereOrigin.x)+(this.y-sphereOrigin.y)*(this.y-sphereOrigin.y)+(this.z-sphereOrigin.z)*(this.z-sphereOrigin.z)) then
                return false
            endif
            return true
        endmethod
        
        method toString takes nothing returns string
            return "("+I2S(R2I(x))+", "+I2S(R2I(y))+", "+I2S(R2I(z))+")"
        endmethod
    endstruct

endlibrary

JASS:
library Stack


module Stack
//******************************
//* Stack Module
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    public static thistype array    stackdata
    public static integer           stacksize       = 0
    public integer                  stackindex
//* ____________________________________________________________________________________________
    method stackremove takes nothing returns nothing
        set stacksize = stacksize - 1
        set stackdata[stackindex] = stackdata[stacksize]
        set stackdata[stackindex].stackindex = stackindex
    endmethod
//* ____________________________________________________________________________________________
    method stackadd takes nothing returns nothing
        set stackdata[stacksize] = this
        set stackindex = stacksize
        set stacksize = stacksize + 1
    endmethod
//*
//**********************************************************************************************
endmodule


endlibrary

JASS:
library ParticleInterface


interface particleinterface
//******************************
//* Particle Interface
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    method  onGround        takes nothing returns nothing       defaults nothing
    method  onGroundLeave   takes nothing returns nothing       defaults nothing
//*
//**********************************************************************************************
endinterface


endlibrary

JASS:
library Particle requires ParticleInterface, PMotion, PData


struct particle extends particleinterface
//******************************
//* Particle
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    public static timer             looptmr         = CreateTimer()
    public static constant real     looptmrfreq     = 0.015
//* ____________________________________________________________________________________________
    implement   AutoData
//*     @ .me
    implement   PMotion
//*     @ .coord
//*     @ .veloc
//*     @ .accel
//*     @ .timescale
    implement   PData
//*     @ .mass
//*     @ .bounce
//*     @ .friction
    implement   Stack
//*     @ .stackdata
//*     @ .stacksize
//*     @ .stackindex
//* ____________________________________________________________________________________________
    method applyForce takes vector forceVector returns nothing
        local vector v = forceVector.copy()
        call v.scale(looptmrfreq)
        set veloc.x = veloc.x + v.x
        set veloc.y = veloc.y + v.y
        set veloc.z = veloc.z + v.z
        call v.destroy( )
    endmethod
//* ____________________________________________________________________________________________
    method onDestroy takes nothing returns nothing
        call killvectors( )
        call stackremove( )
        if (stacksize == 0) then
            call PauseTimer(looptmr)
        endif
    endmethod   
//* ____________________________________________________________________________________________
    static method loopfunc takes nothing returns nothing
        local integer  i = stacksize - 1
        local thistype p
        loop
            exitwhen (i < 0)
            set p = stackdata[i]
            if (p != 0) then
                // Update vector components.
                call p.updatevectors( )
                
                if (p == 1) then
                    call BJDebugMsg("Velocity ("+R2S(p.veloc.x)+", "+R2S(p.veloc.y)+", "+R2S(p.veloc.z)+")")
                    call BJDebugMsg("Coordinates ("+R2S(p.coord.x)+", "+R2S(p.coord.y)+", "+R2S(p.coord.z)+")")
                endif
                
                // Inflict any friction forces that may apply.
                if (p.flagGround) and (p.coord.z <= UnitZ_tempLocZ) then
                    set p.veloc.x = p.veloc.x * p.friction * p.timescale
                    set p.veloc.y = p.veloc.y * p.friction * p.timescale
                    set p.veloc.z = 0
                endif
                
                // Execute interface methods.
                if not (p.flagGround) and (p.coord.z <= UnitZ_tempLocZ) then
                    set p.flagGround = true
                    call p.onGround( )
                    
                elseif (p.flagGround) and (p.coord.z > UnitZ_tempLocZ) then
                    set p.flagGround = false
                    call p.onGroundLeave( )
                    
                endif
            endif
            set i = i - 1
        endloop
    endmethod
//* ____________________________________________________________________________________________
    static method create takes unit u, boolean typeflag returns thistype
        local thistype p = allocate()
        set p.me = u
        
        if (stacksize == 0) then
            call TimerStart(looptmr, looptmrfreq, true, function thistype.loopfunc)
        endif
        call p.setupvectors()           // Gives accessibility to 3 vectors defined in PMotion.
        call p.stackadd()               // Adds particle to stack via Stack module.
        if (typeflag) then
            call UnitAddAbility(u, 'Aloc')
        endif
        call UnitAddAbility(u, 'Amrf')
        call UnitRemoveAbility(u, 'Amrf')

        return p
    endmethod
//*
//**********************************************************************************************
endstruct


endlibrary

JASS:
library PMotion requires Vector, UnitZ


globals
//******************************    
//* Definitions
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    public constant real    def__GRAVITY    = -750
//*
//**********************************************************************************************
endglobals


module PMotion
//******************************
//* PMotion Module
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    public  vector      coord
    public  vector      veloc
    public  vector      accel
//*
    private real        timescaleref    = 1.000
    private boolean     isPaused        = false
//*
//* ____________________________________________________________________________________________
    method operator timescale takes nothing returns real
        if (isPaused) then
            return 0.00
        else
            return timescaleref
        endif
    endmethod
    method operator timescale= takes real n returns nothing
        if (n == 0.00) then
            set isPaused = true
        else
            set timescaleref = n
        endif
    endmethod
//* ____________________________________________________________________________________________
    method updatevectors takes nothing returns nothing
        if not (isPaused) then
            set veloc.x = veloc.x + accel.x * timescaleref
            set veloc.y = veloc.y + accel.y * timescaleref
            set veloc.z = veloc.z + accel.z * timescaleref
            set coord.x = coord.x + veloc.x * timescaleref
            set coord.y = coord.y + veloc.y * timescaleref
            set coord.z = coord.z + veloc.z * timescaleref
            call SetUnitX(me, coord.x)
            call SetUnitY(me, coord.y)
            call SetUnitZ(me, coord.z)
            if (coord.z <= UnitZ_tempLocZ) then
                set coord.z = UnitZ_tempLocZ
            endif
        endif
    endmethod
//* ____________________________________________________________________________________________
    method killvectors takes nothing returns nothing
        call coord.destroy( )
        call veloc.destroy( )
        call accel.destroy( )
    endmethod
//* ____________________________________________________________________________________________
    method setupvectors takes nothing returns nothing
        set coord = vector.create(GetUnitX(me), GetUnitY(me), GetUnitZ(me))
        set veloc = vector.create(0, 0, 0)
        set accel = vector.create(0, 0, def__GRAVITY * looptmrfreq * looptmrfreq)
    endmethod
//*
//**********************************************************************************************
endmodule


endlibrary

JASS:
library PData


globals
//******************************
//* Definitions
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    public constant real    def__MASS       = 20
    public constant real    def__BOUNCE     = 0.98
    public constant real    def__FRICTION   = 0.9
//*
//**********************************************************************************************
endglobals


module PData
//******************************
//* PData Module
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    public real     mass        = def__MASS
    public real     bounce      = def__BOUNCE
    public real     friction    = def__FRICTION
//*
    public boolean  flagGround  = false
//*
//**********************************************************************************************
endmodule


endlibrary

JASS:
library UnitZ


globals
//******************************
//* Definitions
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    public location     tempLoc     = Location(0, 0)
    public real         tempLocZ
//*
//**********************************************************************************************
endglobals

//**
//* Returns the -absolute- Z-coordinate of a unit based on its fly height and the height
//* of the terran below the unit.
//*
function GetUnitZ takes unit u returns real
    call MoveLocation(tempLoc, GetUnitX(u), GetUnitY(u))
    set tempLocZ = GetLocationZ(tempLoc)
    return tempLocZ + GetUnitFlyHeight(u)
endfunction

//**
//* Adjusts a unit's fly-height based on an absolute Z-coordinate value "h" and the height
//* of the terran its on.
//*
function SetUnitZ takes unit u, real h returns nothing
    call MoveLocation(tempLoc, GetUnitX(u), GetUnitY(u))
    set tempLocZ = GetLocationZ(tempLoc)
    call SetUnitFlyHeight(u, h-tempLocZ, 0)
endfunction


endlibrary

JASS:
library Field requires FieldInterface


struct field extends fieldinterface
//******************************
//* Field
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    public real     x
    public real     y
    public real     z
    public real     magnitude
    public real     radius
//* ____________________________________________________________________________________________
    public static timer             looptmr         = CreateTimer()
    public static constant real     looptmrfreq     = 0.015
//* ____________________________________________________________________________________________
    implement   Stack
//* ____________________________________________________________________________________________
    static method loopfunc takes nothing returns nothing
        local integer i = stacksize - 1
        local integer j
        local field f
        local particle p
        local vector vf
        local real distance
        local real amplitude
        loop
            exitwhen (i < 0)
            set f = stackdata[i]
            if (f != 0) then
            
                set j = particle.stacksize - 1
                loop
                    exitwhen (j < 0)
                    set p = particle.stackdata[j]
                    if (p != 0) then
                        
                        set distance = SquareRoot(      /*
                                */ (f.x - p.coord.x) * (f.x - p.coord.x) + /*
                                */ (f.y - p.coord.y) * (f.y - p.coord.y) + /*
                                */ (f.z - p.coord.z) * (f.z - p.coord.z) )
                        
                        if (distance <= f.radius) and (f.particleFilter(p)) then
                            set vf = vector.create(f.x-p.coord.x, f.y-p.coord.y, f.z-p.coord.z)
                            set amplitude = (f.radius - distance) * (f.magnitude/f.radius)
                            call vf.setLength(amplitude)
                            call p.applyForce(vf)
                            call vf.destroy( )
                        endif
                        
                    endif
                    set j = j - 1
                endloop
                
            endif
            set i = i - 1
        endloop
    endmethod   
//* ____________________________________________________________________________________________
    method onDestroy takes nothing returns nothing
        call stackremove( )
        if (stacksize == 0) then
            call PauseTimer(looptmr)
        endif
    endmethod
//* ____________________________________________________________________________________________
    static method create takes real x, real y, real z, real magnitude, real radius returns thistype
        local thistype f = allocate()
        set f.x = x
        set f.y = y
        set f.z = z
        set f.magnitude = magnitude
        set f.radius = radius
        
        if (stacksize == 0) then
            call TimerStart(looptmr, looptmrfreq, true, function thistype.loopfunc)
        endif
        call f.stackadd()
        
        return f
    endmethod
//*
//**********************************************************************************************
endstruct


endlibrary

JASS:
library FieldInterface


interface fieldinterface
//******************************
//* Field Interface
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    method  particleFilter      takes particle p returns boolean    defaults true
//*
//**********************************************************************************************
endinterface


endlibrary
 

Attachments

  • PhysicsDev v2.0.w3x
    64.2 KB · Views: 52
Level 19
Joined
Feb 4, 2009
Messages
1,313
nice
but it will just start to become funny if you take things like friction, spin and collision with other balls into account

and I've got a question
why
JASS:
function GetUnitZ takes unit u returns real
    call MoveLocation(tempLoc, GetUnitX(u), GetUnitY(u))
    set tempLocZ = GetLocationZ(tempLoc)
    return tempLocZ + GetUnitFlyHeight(u)
endfunction

function SetUnitZ takes unit u, real h returns nothing
    call MoveLocation(tempLoc, GetUnitX(u), GetUnitY(u))
    set tempLocZ = GetLocationZ(tempLoc)
    call SetUnitFlyHeight(u, h-tempLocZ, 0)
endfunction
instead of
JASS:
function GetUnitZ takes unit u returns real
    call MoveLocation(tempLoc, GetUnitX(u), GetUnitY(u))
    return GetLocationZ(tempLoc) + GetUnitFlyHeight(u)
endfunction

function SetUnitZ takes unit u, real h returns nothing
    call MoveLocation(tempLoc, GetUnitX(u), GetUnitY(u))
    call SetUnitFlyHeight(u, h-GetLocationZ(tempLoc), 0)
endfunction
?
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
I frequently use tempLocZ in the code to refer to the height of the terrain where the unit (particle) is moved to. Instead of having to either use GetLocationZ(tempLoc) or even worse make a new local location to do this operation. Instead it's easier just to store it with a variable (if you look in the main particle loop, you'll see it's used every iteration).

but it will just start to become funny if you take things like friction, spin and collision with other balls into account

Yea... the WarCraft III engine is pretty terrible for this kind of thing. Currently I have no particle collision (as I said, it was kind of made for simple special effects), but once you throw that enormous amount of enumeration into the mix it really lags down the game. I was able to lag the engine with about 6 fields and ~110 particles and there is almost no substance to the operations executed in the loops.

I don't know how far I'm going to take this, the WarCraft III engine is just too inadequate to support that kind of engine. The fields themselves don't really do anything other than change numbers in the particle structure - yet this causes lag due to the amount of iterations it has to complete (I believe).

~ Updated ~

I ran the script through the optimizer and it actually makes a significant difference. The way all of the code is split up makes it more difficult for the game to execute normally but once its ran through the optimizer the frame-rate with 400 particles goes from about 30 to an average of 45-50. This is with one field that follows the Paladin's position.

Another factor is the model that is being used for the projectiles. I found that when using the Avatar of Vengeance missile for particles my frame-rate was substantially less than when I used a more static model (less particle emissions). This could be due to my mere 1GB of video memory or the game engine.
 
Last edited:
Status
Not open for further replies.
Top