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

Physics Library

Status
Not open for further replies.
Level 18
Joined
Jan 21, 2006
Messages
2,552
Since I had absolutely nothing to do last night, I started coding a physics engine. It is in early stages right now, and it doesn't have many of the features that I would like to include; but that won't stop me from posting some preliminary code and gaining some feedback.

The strength of this (as opposed to a large system such as Simple Entity Engine) is primarily the field struct, which allows users to relatively easily create force-fields that attract particles (types of particles that are attracted can be filtered). It also has some basic interface methods for applying a force to a particle.

Well, whatever, I'll answer any questions if there are any. If anybody has ideas as to what I can add I'd also be happy to hear your thoughts.

JASS:
library Physics initializer init requires Vector

    //############################################################################################
    globals
        // Config Constants
        // ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
        //  Constants that are frequently used by the engine and can be considered user preference
        //  variables. They can be adjusted to create a variation of results.
        //
        //  Period it takes to update each particle. The particles are updated on a stack that is
        //  iterated through each "core__refTime" period.
        private         constant real           core__refTime       = 0.03
        //
        //  When particles are created, they are appointed vectors to store necessary data to
        //  calculate movement patterns.
        private         vector                  def__velocity
        private         vector                  def__acceleration
        //
        private         constant real           def__absorbRadius   = 80
        private         constant real           def__frictionFactor = 0.8
        //
        // Core Engine Components
        // ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
        //  Core engine components.
        private         timer                   core__tmr           = CreateTimer()
        //
        private         hashtable               core__table         = InitHashtable()
        //
        // Dynamic/Temporary Data
        // ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
        //  The following variables are used for quick-reference data storage for performance
        //  reasons. It is faster to reference globals than allocate new local data.
        private         constant location       core__tempLoc       = Location(0, 0)
        private         real                    core__tempLocZ 
        //
    endglobals
    
    //############################################################################################
    function GetUnitZ takes unit u returns real
        //Returns the terrain-height beneath the specified unit along with it's flying height
        call MoveLocation(core__tempLoc, GetUnitX(u), GetUnitY(u))
        set core__tempLocZ = GetLocationZ(core__tempLoc)
        return core__tempLocZ+GetUnitFlyHeight(u)
    endfunction
    function SetUnitZ takes unit u, real z returns nothing
        //Changes a unit's absolute height value
        call MoveLocation(core__tempLoc, GetUnitX(u), GetUnitY(u))
        set core__tempLocZ = GetLocationZ(core__tempLoc)
        call SetUnitFlyHeight(u, z-core__tempLocZ, 0)
    endfunction

    //############################################################################################
    private interface fieldinterface
        method      onAbsorb            takes particle p returns nothing    defaults nothing
        method      particleFilter      takes particle p returns boolean    defaults true
    endinterface
    
    //############################################################################################
    private interface particleinterface
        method      onGroundEvent       takes nothing returns nothing       defaults nothing
        method      onGroundLeaveEvent  takes nothing returns nothing       defaults nothing
    endinterface
    
    //############################################################################################
    // Field
    // ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    //  The field data struct is used to create a force-field which applies a force to affected
    //  particles on each loop iteration.
    //
    struct field extends fieldinterface
    
        readonly        real            x
        readonly        real            y
        readonly        real            z
        readonly        real            magnitude       = 0
        readonly        real            radius
        
        public          boolean         absorbActive    = false
        readonly        real            absorbRange     = def__absorbRadius
        
        private static  timer           core__tmr       = CreateTimer()
        private static  group           core__enum      = CreateGroup()
        private static  thistype        core__temp  
        private static  thistype array  core__stack
        private static  integer         core__stackSize = 0
        private         integer         core__stackDex
        
        //========================================================================================
        method setpos takes real x, real y, real z returns nothing
            set this.x = x
            set this.y = y
            set this.z = z
        endmethod
        
        //========================================================================================
        method onDestroy takes nothing returns nothing
            set thistype.core__stackSize = thistype.core__stackSize-1
            set thistype.core__stack[core__stackDex] = thistype.core__stack[thistype.core__stackSize]
            set thistype.core__stack[core__stackDex].core__stackDex = core__stackDex
            if(thistype.core__stackSize == 0) then
                call PauseTimer(thistype.core__tmr)
            endif
        endmethod
        
        //========================================================================================
        private static method loopEnumFunc takes nothing returns boolean
            local particle p = particle[GetFilterUnit()]
            local thistype f = core__temp
            local vector   v
            local real     m
            local real     d
            
            if (p != 0) and (f.particleFilter(p)) then
                set d = SquareRoot((f.x-p.x)*(f.x-p.x) + (f.y-p.y)*(f.y-p.y) + (f.z-p.z)*(f.z-p.z))
                
                if(f.absorbActive) and(d <= f.absorbRange) then
                    call f.onAbsorb(p)
                endif
                set m = (f.radius - d) * (f.magnitude/f.radius)
            
                set v = vector.create(f.x-p.x, f.y-p.y, f.z-p.z)
                call v.setLength(m)
                call p.applyForce(v)
                
                call v.destroy()
            endif
            
            return false
        endmethod
        
        private static method loopFunc takes nothing returns nothing
            local integer i = core__stackSize-1
            local thistype f
            loop
                exitwhen(i < 0)
                set f = core__stack[i]
                if(f != 0) then
                    set core__temp = f
                    call GroupEnumUnitsInRange(core__enum, f.x, f.y, f.radius,/*
                                        */Filter(function thistype.loopEnumFunc))
                endif
                set i = i-1
            endloop
        endmethod
        
        //========================================================================================
        static method create takes real x, real y, real z, real magnitude, real radius returns thistype
            local thistype f = allocate()
            set f.magnitude  = magnitude
            set f.radius     = radius+0.001
            set f.x          = x
            set f.y          = y
            set f.z          = z
            if(core__stackSize == 0) then
                call TimerStart(thistype.core__tmr, core__refTime, true, function thistype.loopFunc)
            endif
            set f.core__stackDex = core__stackSize
            set core__stack[core__stackSize] = f
            set core__stackSize = core__stackSize+1
            return f
        endmethod
    endstruct
    
    //############################################################################################
    // Particle
    // ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    //  The primary data struct for the physics engine, contains all necessary data for 
    //  controlling the position and physical motion of any created particles.
    //
    struct particle extends particleinterface
    
        readonly        unit            self            = null
        
        private         vector          core__pos       = 0
        private         vector          core__vel       = 0
        private         vector          core__acc       = 0
        
        private         boolean         flag__ground    = false
        
        private         real            ref__timescale  = 1.0
        private         real            ref__mass       
        
        private static  thistype array  core__stack
        private static  integer         core__stackSize = 0
        private         integer         core__stackDex
    
        //========================================================================================
        method setpos takes real x, real y, real z returns nothing
            set core__pos.x = x
            set core__pos.y = y
            set core__pos.z = z
        endmethod
    
        //========================================================================================
        static method operator [] takes unit u returns thistype
            return thistype(LoadInteger(core__table, GetHandleId(u), 1))
        endmethod
    
        method operator x takes nothing returns real
            return core__pos.x
        endmethod
        method operator y takes nothing returns real
            return core__pos.y
        endmethod
        method operator z takes nothing returns real
            return core__pos.z
        endmethod
        
        //========================================================================================
        method reflect takes vector normalVector returns nothing
            call core__vel.rotate(normalVector, bj_PI)
            call core__vel.setLength(-core__vel.getLength())
        endmethod
    
        //========================================================================================
        method onDestroy takes nothing returns nothing
            //Destroy vectors
            call core__pos.destroy()
            call core__vel.destroy()
            call core__acc.destroy()
            //Remove from stack
            set thistype.core__stackSize = thistype.core__stackSize-1
            set thistype.core__stack[core__stackDex] = thistype.core__stack[thistype.core__stackSize]
            set thistype.core__stack[core__stackDex].core__stackDex = core__stackDex
            if(thistype.core__stackSize == 0) then
                call PauseTimer(core__tmr)
            endif
            call RemoveSavedInteger(core__table, GetHandleId(self), 1)
        endmethod
        
        //========================================================================================
        method applyForce takes vector forceVector returns nothing
            local vector v = forceVector.copy()
            call v.scale(core__refTime)
            call core__vel.add(v)
            call v.destroy()
        endmethod
        
        //========================================================================================
        debug   method debug takes nothing returns nothing
        debug       //call BJDebugMsg(R2S(core__acc.getLength()))
        debug       //call BJDebugMsg(R2S(core__vel.getLength()))
        debug   endmethod
    
        //========================================================================================
        //Iterates through particles that are included in the data stack.
        private static method loopFunc takes nothing returns nothing
            local integer i = thistype.core__stackSize-1
            local thistype p
            local vector acc
            local vector vel
            local vector pos
            loop
                exitwhen(i < 0)
                set p = thistype.core__stack[i]
                if(p != 0)then
                    //Update vectors
                    set acc = p.core__acc
                    set vel = p.core__vel
                    set pos = p.core__pos
                    set vel.x = vel.x + acc.x * p.ref__timescale
                    set vel.y = vel.y + acc.y * p.ref__timescale
                    set vel.z = vel.z + acc.z * p.ref__timescale
                    set pos.x = pos.x + vel.x * p.ref__timescale
                    set pos.y = pos.y + vel.y * p.ref__timescale
                    set pos.z = pos.z + vel.z * p.ref__timescale
                    
                    //Debug output
                    debug call p.debug()
                    
                    //Update unit position
                    call SetUnitX(p.self, p.core__pos.x)
                    call SetUnitY(p.self, p.core__pos.y)
                    call SetUnitZ(p.self, p.core__pos.z)
                    call SetUnitLookAt(p.self, "BONE_HEAD", p.self, vel.x, vel.y, vel.z)
                    
                    if(p.core__pos.z <= core__tempLocZ) and not(p.flag__ground) then
                        //The particle has made contact with the terrain
                        set p.flag__ground = true
                        call p.onGroundEvent()
                        
                    elseif(p.core__pos.z > core__tempLocZ) and(p.flag__ground) then
                        //The particle has escaped from being in contact with the terrain
                        set p.flag__ground = false
                        call p.onGroundLeaveEvent()
                        
                    endif
                endif
                set i = i-1
            endloop
        endmethod
    
        //========================================================================================
        //Constructs a particle given a specific unit. In addition if "change" is flagged then the
        //Locust and Chaos abilities will be added to it so that it is unselectable.
        static method create takes unit u, boolean change returns thistype
            local thistype p = allocate()
            set p.self = u
            set p.core__pos = vector.create(GetUnitX(u), GetUnitY(u), GetUnitZ(u))
            set p.core__vel = def__velocity.copy()
            set p.core__acc = def__acceleration.copy()
            //Scale velocity/acceleration vectors to match the loop period
            call p.core__acc.scale(core__refTime*core__refTime)
            call p.core__vel.scale(core__refTime)
            
            //Save the data to the unit in a hashtable so it can be quickly referenced externally
            call SaveInteger(core__table, GetHandleId(u), 1, p)
            
            if(core__stackSize == 0) then
                call TimerStart(core__tmr, core__refTime, true, function thistype.loopFunc)
            endif
            set core__stack[core__stackSize] = p
            set p.core__stackDex = core__stackSize
            set core__stackSize = core__stackSize+1
            
            if(change) then 
                call UnitAddAbility(u, 'Aloc')
                call UnitAddAbility(u, 'Srtt')
                call UnitRemoveAbility(u, 'Aloc')
            endif
            call UnitAddAbility(u, 'Amrf')
            call UnitRemoveAbility(u, 'Amrf')
            
            return p
        endmethod
    endstruct
    
    //############################################################################################
    private function init takes nothing returns nothing
        //Setup default vector references
        set def__velocity = vector.create(0, 0, 0)
        set def__acceleration = vector.create(0, 0, 0)
    endfunction

endlibrary
 
Status
Not open for further replies.
Top