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