├── .gitattributes ├── .gitignore ├── README.md ├── platform └── scripts │ └── sfm │ └── animset │ ├── Run_sfmphys_Simulation.py │ └── rig_physics.py ├── sdktools └── python │ └── 2.7 │ └── win32 │ └── Lib │ └── site-packages │ ├── sfmphys │ ├── __init__.py │ ├── bullet_utils.py │ ├── dagutils.py │ ├── rigutils.py │ └── sessionutils.py │ └── swigbullet │ ├── ___init__.pyd │ └── __init__.py ├── usermod ├── materials │ ├── lambert.vtf │ └── lambert1.vmt └── models │ └── narry │ ├── cloth_test.dx80.vtx │ ├── cloth_test.dx90.vtx │ ├── cloth_test.mdl │ ├── cloth_test.physics.txt │ ├── cloth_test.sw.vtx │ └── cloth_test.vvd └── windrunner_cape.physics.txt /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimPiko/sfmphys/95edba974fd7e7b0e93af78aadf7b4a5f762c2b1/.gitignore -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Changelog 2 | Update 19 August 2013 3 | * Fixed some errors in the sim script 4 | 5 | Update 13 August 2013 6 | 7 | * Updated to Python 2.7.5 (for the July 31 SFM update) 8 | * Updated all guis to use PySide qt bindings 9 | * Moved phys_simulate to the global scripts menu; it is no longer a "rig" script 10 | * Started work on some fancy GUI things -- not included in this update, just thought I'd mention. 11 | 12 | Update 2 May 2013 13 | 14 | * Updated to Python 2.7 (for the May 1 SFM update) 15 | * Added a "mass" attribute for rigid and soft bodies, accessible through the *element viewer*. The default of 1 should serve well enough for most props, but for exceptionally large or small props (or if you want to simulate, say, something like a seesaw) it's now there for you to adjust. See also: large cloth needs extra mass to scale well, so if you're working with large cloth and things pass through or break entirely, consider adjusting the mass. 16 | 17 | Update 23 April 2013 18 | 19 | * If you are updating to this version from a previous version, try to remove the files from the previous version to avoid any conflicts; there aren't be any specific problems that I am aware of, but just to be sure. 20 | * bullet_server is no longer needed. Just run the scripts and go! 21 | * better automatic rigging for ragdolls, and more configurable joint constraints 22 | * soft-body physics, with an emphasis on cloth (and the possibility for some other limited applications) 23 | 24 | Older versions 25 | 26 | * Nyeh, I didn't have a changelog. And it's not all that interesting. Just pretend I started with the April 23 release if it makes any difference :P. 27 | 28 | #SFM Physics Scripts 29 | ### What does this do? 30 | * Incorporates physics simulation into SFM via python scripts 31 | * Handles collision detection and response 32 | * Can create physics-controlled, static, and kinematic bodies 33 | * Can create ragdolls and ropes, assuming the source model is set up with proper hitboxes 34 | * Can simulate cloth physics, given a properly set-up model 35 | * You can adjust the density, bounciness, and friction of objects 36 | * You can animate external forces on objects 37 | * It *might* crash SFM. If you're concerned, *backup your important files*, just in case. 38 | 39 | ### What does this not do? (Planned or Impractical Features) 40 | * Handle collisions with the level 41 | * You can get around this by adding kinematic objects where the level geometry is. (Models do not have to be visible to be included in the simulation!) 42 | * Use .mdl or collision meshes 43 | * Only the bounding boxes are used for simulation. Support for capsules and spheres will be added at a later date. 44 | * Loading from .mdl probably isn't going to happen. Loading from .smd or other formats might, but not for a while. 45 | * Springs, explosions, etc. 46 | * These will (hopefully) be in a later release 47 | 48 | ### How to install 49 | * Grab this repository with git, or download the .zip ("Download ZIP" button in the right margin) 50 | * Extract and copy the "platform" and "sdktools" folders to [steamapps]/common/SourceFilmmaker/game. Choose to merge folders. 51 | 52 | ### How to use 53 | * Start sfm, and set up your scene. Attach a rig_physics to any objects you want included in the simulation. 54 | * The "shape" control works as follows: if value < 0.5 (slider on the left half), "box" is selected; if value > 0.5 (slider on the right half), "sphere" is selected. 55 | * The "kinematic" control is "dynamic" for values < 0.5; "kinematic" for values > 0.5. 56 | * The "center of mass" control currently does not function properly, and the "force" control will need some tweaking to be more useful (note that you can also rotate the "force" control to apply torque to an object). 57 | * The "damping" controls determine how quickly objects lose momentum over time. If objects are shaking uncontrollably, try increasing the damping; if they are moving too slowly, try decreasing. The defaults are generally fine for rigidbodies; you may want to increase for ragdolls, and cloth physics will require tweaking on an individual basis. 58 | * Select a period of time to simulate over in the motion editor (ie, with the floating modification layer). The simulation will not run if the time selection is infinite in either direction. 59 | * Once the scene is set up, right-click any animation set and run "Run sfmphys Simulation". The simulation might take a few seconds. If SFM doesn't respond immediately just be patient. 60 | * To run a simulation again, just run "Run sfmphys Simulation" again. There's no need to re-rig objects (the sim is reset automatically every time "Run sfmphys Simulation" runs). 61 | 62 | ### Some notes on ragdolls 63 | * Animation with ragdolls is difficult since you have to use the physics handles. I'll add an option to use the model's normal bones for fully-kinematic objects in the future. 64 | * Ropes use the positions of bones at the start of the time selection to determine how joints should line up. So for the best results, leave the model in its default position (ie, t-pose for ragdolls, straight line for ropes) and then animate any kinematic bones into position before the scene starts and give the sim a second to stabilize. 65 | 66 | ### Some notes on cloth 67 | * You *need* a .physics.txt file to create cloth bodies. An example cloth model is included in the /usermod/models/narry/ folder. Another file, windrunner_cape.physics.txt is also included as an example of rigging a cloth model from DOTA 2. The "width" and "height" parameters are self-explanatory; the "boneformat" parameter is described in more detail at http://docs.python.org/2/library/string.html#format-examples. The script iterates each variable {0}, {1}, ..., {n} over the ranges provided in "formatranges" and creates a bone for each. The order of iteration is to increment {0} first, then {1}, etc. The cloth is assumed to always be a 2D plane with dimensions defined by the "width" and "height" parameters regardless of how many variables are in the "boneformat" string. 68 | * To make kinematic cloth, open the rigged animation set in the Element Viewer and navigate to "Root Control Group -> children -> SoftBodies -> children -> (cloth name)" and open the node list. From there you can edit the masses of nodes (use 1 for dynamic and 0 for kinematic). If you're feeling adventurous, feel free to poke around the other parts of the physics rigs in the element viewer. There are some parameters there that I either didn't or couldn't turn into sliders for the rig. 69 | 70 | ### Videos 71 | Day 1 http://www.youtube.com/watch?v=LDMB95El9GA 72 | Day 2 http://www.youtube.com/watch?v=a99sJrXWOxo 73 | Day 3 http://www.youtube.com/watch?v=GtQ50YWORcA 74 | Day 5 http://www.youtube.com/watch?v=vyzgnJhkdeQ 75 | Day 5.5 http://www.youtube.com/watch?v=AyKmftF3ZX0 76 | Day whatever: http://www.youtube.com/watch?v=-GgTciTTUs8 77 | Day whatever + 1: http://www.youtube.com/watch?v=6ELrKtixYog 78 | Day cloth: http://www.youtube.com/watch?v=zY2TTc0GKh4 79 | Day cloth2: http://www.youtube.com/watch?v=uy2ICpS0znw 80 | Cloth Parameters Reference: http://www.youtube.com/watch?v=AxK6saPpbW8 81 | 82 | This project uses the bullet physics library. Check out http://bulletphysics.org for more info. 83 | The included python bindings for bullet are based on https://github.com/ousttrue/swigbullet 84 | See the bindings plus modifications at https://github.com/btdavis/swig-bullet 85 | Credit for the included example cloth model goes to OSFM member "Narry Gewman" 86 | -------------------------------------------------------------------------------- /platform/scripts/sfm/animset/Run_sfmphys_Simulation.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import time 3 | import vs 4 | import sfmUtils 5 | import sfmClipEditor 6 | 7 | from PySide import QtCore 8 | from PySide import QtGui 9 | 10 | import sfmphys.dagutils 11 | sfmphys.dagutils = reload(sfmphys.dagutils) 12 | import sfmphys.sessionutils 13 | sfmphys.sessionutils = reload(sfmphys.sessionutils) 14 | import sfmphys.rigutils 15 | sfmphys.rigutils = reload(sfmphys.rigutils) 16 | import sfmphys.bullet_utils 17 | sfmphys.bullet_utils = reload(sfmphys.bullet_utils) 18 | 19 | from sfmphys.dagutils import * 20 | from sfmphys.rigutils import * 21 | from sfmphys.sessionutils import * 22 | from sfmphys.bullet_utils import * 23 | 24 | if len(sfmClipEditor.GetSelectedShots()) == 1: 25 | setCurrentShot(sfmClipEditor.GetSelectedShots()[0]) 26 | else: 27 | raise Exception("phys_simulate.py: Please select exactly one shot.") 28 | 29 | #get info about the shot 30 | timeSelection = GetCurrentTimeSelection() 31 | 32 | #check for "infinite" 33 | if (timeSelection.IsEitherInfinite()): 34 | raise Exception("phys_simulate.py: Please select a finite time range.") 35 | 36 | currenttime = timeSelection.GetValue("hold_left") 37 | dt = vs.DmeTime_t(1.0 / GetFrameRate()) 38 | 39 | #create a physics world 40 | world = World() 41 | 42 | #see which animsets have phys rigs applied and grab the relevant info 43 | rigidBodies = {} 44 | softBodies = [] 45 | constraints = [] 46 | 47 | for animSet in GetAnimationSets(): 48 | root_group = animSet.GetRootControlGroup() 49 | 50 | if root_group.HasChildGroup("Rigidbodies", False): 51 | phys_group = root_group.FindChildByName("Rigidbodies", False) 52 | 53 | for group in phys_group.GetValue("children"): 54 | bodyrig = RigidbodyRig(group=group, time=currenttime) 55 | bodyrig.body = Rigidbody(bodyrig) 56 | world.addRigidBody(bodyrig.body) 57 | rigidBodies[animSet.GetName()+":"+bodyrig.target] = bodyrig 58 | #end 59 | #end 60 | 61 | if root_group.HasChildGroup("PhysConstraints", False): 62 | phys_group = root_group.FindChildByName("PhysConstraints", False) 63 | 64 | for group in phys_group.GetValue("children"): 65 | consrig = ConstraintRig(group=group, time=currenttime) 66 | bodya = rigidBodies[animSet.GetName()+":"+consrig.bodya].body 67 | bodyb = rigidBodies[animSet.GetName()+":"+consrig.bodyb].body 68 | consrig.cons = Constraint(consrig, bodya, bodyb) 69 | world.addConstraint(consrig.cons) 70 | constraints.append(consrig) 71 | #end 72 | #end 73 | 74 | if root_group.HasChildGroup("Softbodies", False): 75 | phys_group = root_group.FindChildByName("Softbodies", False) 76 | 77 | for group in phys_group.GetValue("children"): 78 | softrig = SoftbodyRig(group=group, time=currenttime) 79 | softrig.body = Softbody(softrig, world.getWorldInfo()) 80 | world.addSoftBody(softrig.body) 81 | softBodies.append(softrig) 82 | #end 83 | #end 84 | #end 85 | 86 | if (len(rigidBodies) == 0 and len(softBodies) == 0): 87 | raise Exception("phys_simulate.py: No animation sets with a rig_physics found.") 88 | 89 | #do the simulation over this time period 90 | t_left = timeSelection.GetValue("hold_left").GetSeconds() 91 | t_right = timeSelection.GetValue("hold_right").GetSeconds() 92 | nframes = (t_right - t_left) / dt.GetSeconds() 93 | 94 | #create a window for a progress bar 95 | app = QtGui.QApplication.instance() 96 | window = QtGui.QWidget() 97 | window.resize(500, 25) 98 | window.setWindowTitle('Simple') 99 | 100 | progressbar = QtGui.QProgressBar(window) 101 | progressbar.resize(500,25) 102 | progressbar.setMinimum(0) 103 | progressbar.setMaximum(nframes) 104 | progressbar.setValue(0) 105 | 106 | window.show() 107 | 108 | currentFrame = 0 109 | def doFrame(): 110 | global currentFrame, currenttime, nframes 111 | global rigidBodies, softBodies 112 | global world 113 | global window 114 | global progressbar 115 | 116 | if currentFrame >= nframes: 117 | window.close() 118 | return 0 119 | #end 120 | 121 | progressbar.setValue(currentFrame) 122 | 123 | #move objects 124 | for key, b in rigidBodies.iteritems(): 125 | if b.mass == 0: 126 | trans = GetAbsTransformAtTime(b.handle, currenttime) 127 | pos, quat = TransformToPosQuat(trans) 128 | b.body.setTransform(pos, quat) 129 | else: 130 | trans = GetTransformAtTime(b.force, currenttime) #LOCAL transform 131 | pos, rot = TransformToPosEuler(trans) 132 | b.body.addForce(pos, rot) 133 | #end 134 | #end 135 | 136 | for b in softBodies: 137 | for i in range(len(b.nodelist)): 138 | node = b.nodelist[i] 139 | dag = b.daglist[i] 140 | if node[1] == 0: 141 | trans = GetAbsTransformAtTime(dag, currenttime) 142 | pos, quat = TransformToPosQuat(trans) 143 | b.body.setPosition(i, pos) 144 | #end 145 | #end 146 | #end 147 | 148 | #update the simulation 149 | world.stepWorld(dt.GetSeconds()) 150 | 151 | #grab new positions of dynamic objects 152 | for key, b in rigidBodies.iteritems(): 153 | if (b.mass != 0): 154 | pos, quat = b.body.getTransform() 155 | trans = PosQuatToTransform(pos, quat) 156 | SetAbsTransformAtTime(b.handle, currenttime, trans) 157 | #end 158 | #end 159 | 160 | for b in softBodies: 161 | for i in range(len(b.nodelist)): 162 | node = b.nodelist[i] 163 | dag = b.daglist[i] 164 | if node[1] != 0: 165 | oldtrans = GetAbsTransformAtTime(dag, currenttime) 166 | oldpos, oldquat = TransformToPosQuat(oldtrans) 167 | newtrans = PosQuatToTransform(b.body.getPosition(i), oldquat) 168 | SetAbsTransformAtTime(dag, currenttime, newtrans) 169 | #end 170 | #end 171 | #end 172 | 173 | currenttime += dt 174 | currentFrame+=1 175 | 176 | time.sleep(0) 177 | 178 | return 1 179 | #end 180 | 181 | while (doFrame()): 182 | pass 183 | 184 | sys.stderr.write("phys_simulate.py: cleaning up\n") 185 | world.destroy() 186 | del rigidBodies 187 | del softBodies 188 | del constraints 189 | -------------------------------------------------------------------------------- /platform/scripts/sfm/animset/rig_physics.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | 5 | import vs 6 | import sfmUtils 7 | from filesystem import * 8 | 9 | import sfmphys.rigutils 10 | sfmphys.rigutils = reload(sfmphys.rigutils) 11 | 12 | from sfmphys.rigutils import * 13 | from sfmphys.dagutils import * 14 | 15 | def unicodeToStr(data): 16 | if isinstance(data, dict): 17 | return dict((unicodeToStr(key), unicodeToStr(value)) for (key, value) in data.iteritems()) 18 | elif isinstance(data, list): 19 | return [unicodeToStr(element) for element in data] 20 | elif isinstance(data, unicode): 21 | return data.encode('utf-8') 22 | else: 23 | return data 24 | #end 25 | 26 | def vectorToList(vec): 27 | return [vec.x, vec.y, vec.z] 28 | #end 29 | def listToVector(tup): 30 | return vs.Vector(tup[0], tup[1], tup[2]) 31 | #end 32 | def quatToList(quat): 33 | return [quat.x, quat.y, quat.z, quat.w] 34 | #end 35 | def listToQuat(tup): 36 | return vs.Quaternion(tup[0], tup[1], tup[2], tup[3]) 37 | #end 38 | 39 | shot = sfm.GetCurrentShot() 40 | animSet = sfm.GetCurrentAnimationSet() 41 | model = animSet.gameModel 42 | 43 | mdl_path = valve.RelativePathToFullPath(model.GetModelName(), valve.game()) 44 | if mdl_path is None: 45 | phys_path = "" 46 | else: 47 | phys_path = os.path.splitext(mdl_path)[0] + ".physics.txt" 48 | 49 | data = dict() 50 | 51 | sys.stderr.write("rig_physics.py: looking for config file at: "+str(phys_path)+"\n") 52 | 53 | if os.path.isfile(phys_path): 54 | #load data from the file 55 | sys.stderr.write("rig_physics.py: loading config\n") 56 | infile = open(phys_path, "r") 57 | data = unicodeToStr(json.load(infile)) 58 | infile.close() 59 | else: 60 | #try to guess with rigidbodies 61 | sys.stderr.write("rig_physics.py: no config found, assuming rigidbody\n") 62 | hdr = vs.CStudioHdr(model.GetStudioHdr()) 63 | parents = {} 64 | 65 | data["rigidbodies"] = list() 66 | data["constraints"] = list() 67 | 68 | nhitset = hdr.numhitboxsets() 69 | for ihitset in range(nhitset): 70 | nhitbox = hdr.iHitboxCount(ihitset) 71 | for ihitbox in range(nhitbox): 72 | box = hdr.pHitbox(ihitbox, ihitset) 73 | bone = hdr.pBone(box.bone) 74 | bonename = bone.pszName() 75 | boxsize = (box.bbmax - box.bbmin) / 2 76 | boxcenter = (box.bbmax + box.bbmin) / 2 77 | 78 | body = {"target": bonename, 79 | "boxcenter": vectorToList(boxcenter), 80 | "boxsize": vectorToList(boxsize)} 81 | data["rigidbodies"].append(body) 82 | 83 | tempBone = bone 84 | parents[bonename] = [] 85 | while tempBone.parent != -1: 86 | parent = hdr.pBone(tempBone.parent) 87 | parents[bonename].append(parent.pszName()) 88 | tempBone = parent 89 | #end 90 | #end 91 | #end 92 | 93 | #create constraints 94 | for i in parents: 95 | par = parents[i] 96 | for j in par: 97 | if j in parents: #if j is an ancestor of i *and* has an associated rigidbody 98 | cons = {"constype": "cone", 99 | "bodya": j, 100 | "bodyb": i, 101 | "rotx": 90, 102 | "roty": 90, 103 | "twist": 35} 104 | 105 | data["constraints"].append(cons) 106 | break #we can only have 1 parent per bone 107 | #end 108 | #end 109 | #end 110 | 111 | #don't do this yet. 112 | #if the rig doesn't work, it'll save the faulty rig and that's a Bad Thing 113 | #outfile = open(phys_path, "w") 114 | #json.dump(data, outfile, indent=2) 115 | #outfile.close() 116 | #sys.stderr.write("rig_physics.py: saved rigidbody config to "+str(phys_path)+"\n") 117 | #end 118 | 119 | #create the rig 120 | rig = sfm.BeginRig("rig_physics_" + animSet.GetName(), True) 121 | rootGroup = animSet.GetRootControlGroup() 122 | 123 | rigidbodyGroup = rootGroup.CreateControlGroup("Rigidbodies") 124 | constraintGroup = rootGroup.CreateControlGroup("PhysConstraints") 125 | softbodyGroup = rootGroup.CreateControlGroup("Softbodies") 126 | 127 | if "rigidbodies" in data: 128 | for body in data["rigidbodies"]: 129 | body["boxcenter"] = listToVector(body["boxcenter"]) 130 | body["boxsize"] = listToVector(body["boxsize"]) 131 | group = rigidbodyGroup.CreateControlGroup("Body ("+body["target"]+")") 132 | bodyrig = RigidbodyRig(data=body) 133 | bodyrig.writeToGroup(group) 134 | #end 135 | #end 136 | if "constraints" in data: 137 | for cons in data["constraints"]: 138 | group = constraintGroup.CreateControlGroup(cons["constype"] + "_constraint ("+cons["bodya"]+" -> "+cons["bodyb"]+")") 139 | consrig = ConstraintRig(data=cons) 140 | consrig.writeToGroup(group) 141 | #end 142 | #end 143 | 144 | if "cloths" in data: 145 | for cloth in data["cloths"]: 146 | body = dict(cloth) 147 | body["nodelist"] = list() 148 | body["linklist"] = list() 149 | body["facelist"] = list() 150 | 151 | boneformat = cloth["boneformat"] 152 | ranges = cloth["formatranges"] 153 | body["boneprefix"] = boneformat.format(*["" for r in ranges]) 154 | counters = [r[0] for r in ranges] 155 | 156 | while 1: 157 | name = boneformat.format(*counters) 158 | body["nodelist"].append( (name, 1) ) 159 | 160 | endLoop = True 161 | for n in range(len(counters)): 162 | counters[n]+=1 163 | if counters[n] <= ranges[n][1]: 164 | endLoop = False 165 | break 166 | #end 167 | 168 | counters[n] = ranges[n][0] 169 | #end 170 | 171 | #if we go through the for-loop without breaking then the range is completed 172 | if endLoop: 173 | break 174 | #end 175 | 176 | width = cloth["width"] 177 | height = cloth["height"] 178 | 179 | for row in range(height): 180 | for column in range(width): 181 | index = row*width+column 182 | #only create links along face edges 183 | #no shear/bend links, since those are handled more or less by bullet 184 | if row != 0: 185 | body["linklist"].append( (index, index-width) ) 186 | if column != 0: 187 | body["linklist"].append( (index, index-1) ) 188 | 189 | if (row != 0) and (column != 0): 190 | body["facelist"].append( (index, index-1, index-width-1, index-width) ) 191 | #end 192 | #end 193 | 194 | if not "softbodies" in data: 195 | data["softbodies"] = list() 196 | 197 | data["softbodies"].append(body) 198 | #end 199 | #end 200 | 201 | if "softbodies" in data: 202 | for body in data["softbodies"]: 203 | group = softbodyGroup.CreateControlGroup("Softbody ("+body["boneprefix"]+")") 204 | softrig = SoftbodyRig(data=body) 205 | softrig.writeToGroup(group) 206 | #end 207 | #end 208 | 209 | sfmUtils.SetControlGroupColor(rigidbodyGroup, vs.Color(128,255,128,255)) 210 | sfmUtils.SetControlGroupColor(constraintGroup, vs.Color(128,200,200,255)) 211 | sfmUtils.SetControlGroupColor(softbodyGroup, vs.Color(128,128,255,255)) 212 | 213 | sfm.EndRig() 214 | -------------------------------------------------------------------------------- /sdktools/python/2.7/win32/Lib/site-packages/sfmphys/__init__.py: -------------------------------------------------------------------------------- 1 | # This is a package 2 | -------------------------------------------------------------------------------- /sdktools/python/2.7/win32/Lib/site-packages/sfmphys/bullet_utils.py: -------------------------------------------------------------------------------- 1 | import vs 2 | import sys 3 | import math 4 | import sfmUtils 5 | from .dagutils import * 6 | from swigbullet import * 7 | 8 | unitsPerMeter = 53.3 9 | def vsToBtVector(vec): 10 | return btVector3(vec.x/unitsPerMeter, vec.y/unitsPerMeter, vec.z/unitsPerMeter) 11 | #end 12 | def btToVsVector(vec): 13 | return vs.Vector(vec.x()*unitsPerMeter, vec.y()*unitsPerMeter, vec.z()*unitsPerMeter) 14 | #end 15 | 16 | def vsToBtQuaternion(quat): 17 | return btQuaternion(quat.x, quat.y, quat.z, quat.w) 18 | #end 19 | def btToVsQuaternion(quat): 20 | return vs.Quaternion(quat.x(), quat.y(), quat.z(), quat.w()) 21 | #end 22 | 23 | class World: 24 | def __init__(self, gravity=btVector3(0,0,-10), airdensity=0.2): 25 | self.broadphase = btDbvtBroadphase() 26 | self.collisionConfiguration = btSoftBodyRigidBodyCollisionConfiguration() 27 | self.dispatcher = btCollisionDispatcher(self.collisionConfiguration) 28 | self.solver = btSequentialImpulseConstraintSolver() 29 | self.softsolver = btDefaultSoftBodySolver() 30 | self.world = btSoftRigidDynamicsWorld(self.dispatcher, self.broadphase, self.solver, self.collisionConfiguration, self.softsolver) 31 | 32 | self.world.setApplySpeculativeContactRestitution(True) 33 | self.world.setGravity(gravity) 34 | 35 | self.info = btSoftBodyWorldInfo() 36 | self.info.air_density = airdensity 37 | self.info.water_density = airdensity 38 | self.info.m_broadphase = self.broadphase 39 | self.info.m_dispatcher = self.dispatcher 40 | self.info.m_gravity = gravity 41 | self.info.m_sparsesdf.Initialize() 42 | 43 | self.fixedStep = 1.0/180.0 44 | self.elapsedTime = 0 45 | #end 46 | def destroy(self): 47 | del(self.world) 48 | del(self.softsolver) 49 | del(self.solver) 50 | del(self.collisionConfiguration) 51 | del(self.dispatcher) 52 | del(self.broadphase) 53 | #end 54 | 55 | def addRigidBody(self, rigidbody): 56 | self.world.addRigidBody(rigidbody.body) 57 | #end 58 | def addSoftBody(self, softbody): 59 | self.world.addSoftBody(softbody.body) 60 | #end 61 | def addConstraint(self, constraint): 62 | self.world.addConstraint(constraint.cons, True) 63 | #end 64 | 65 | def getFixedTimeStep(self): 66 | return self.fixedStep 67 | #end 68 | def stepWorld(self, dt): 69 | maxsteps = int(1.0/self.fixedStep) 70 | self.info.m_sparsesdf.GarbageCollect() 71 | nsteps = self.world.stepSimulation(dt, maxsteps, self.fixedStep) 72 | self.elapsedTime += dt 73 | 74 | #sys.stderr.write("Elapsed time: "+str(self.elapsedTime)+" (dt="+str(dt)+"; substeps="+str(nsteps)+")\n") 75 | #end 76 | 77 | def getWorldInfo(self): 78 | return self.info 79 | #end 80 | #end 81 | 82 | class Rigidbody: 83 | def __init__(self, data): 84 | #TODO: convex hulls 85 | if (data.shape == "box"): 86 | self.shape = btBoxShape(vsToBtVector(data.boxsize)) 87 | elif (data.shape == "sphere"): 88 | posdata = btVector3Array(1) 89 | posdata[0] = btVector3(0,0,0) 90 | radiusdata = floatArray(1) 91 | radiusdata[0] = 1 92 | 93 | self.shape = btMultiSphereShape(posdata, radiusdata, 1) 94 | self.shape.setLocalScaling(vsToBtVector(data.boxsize)) 95 | 96 | del posdata 97 | del radiusdata 98 | #end 99 | 100 | self.shape.setMargin(data.margin) 101 | 102 | self.motion = btDefaultMotionState(btTransform(vsToBtQuaternion(data.quat), vsToBtVector(data.pos))) 103 | 104 | self.inertia = btVector3(0,0,0) 105 | self.mass = data.mass 106 | self.shape.calculateLocalInertia(self.mass, self.inertia) 107 | 108 | self.body = btRigidBody(self.mass, self.motion, self.shape, self.inertia) 109 | if (self.mass == 0): 110 | self.body.setCollisionFlags(self.body.getCollisionFlags() | btCollisionObject.CF_KINEMATIC_OBJECT) 111 | #end 112 | 113 | self.body.setActivationState(DISABLE_DEACTIVATION) 114 | self.body.setRestitution(data.bounce) 115 | self.body.setFriction(data.friction) 116 | self.body.setDamping(data.lindamp, data.rotdamp) 117 | #end 118 | 119 | def setTransform(self, pos, quat): 120 | self.motion.setWorldTransform(btTransform(vsToBtQuaternion(quat), vsToBtVector(pos))) 121 | #end 122 | 123 | def getTransform(self): 124 | trans = btTransform() 125 | self.motion.getWorldTransform(trans) 126 | return btToVsVector(trans.getOrigin()), btToVsQuaternion(trans.getRotation()) 127 | #end 128 | 129 | def addForce(self, pos, rot): 130 | self.body.applyCentralForce(vsToBtVector(pos)); 131 | self.body.applyTorque(vsToBtVector(rot)); 132 | #end 133 | #end 134 | 135 | def degToRad(angle): 136 | return angle*(math.pi/180.0) 137 | #end 138 | 139 | class Constraint: 140 | def __init__(self, data, bodya, bodyb): 141 | #data.bodya,b are strings 142 | #let the sim script determine which btRigidBody corresponds to a and b 143 | #and pass them as separate parameters 144 | self.bodya = data.bodya 145 | self.bodyb = data.bodyb 146 | 147 | jointTransform = btTransform(vsToBtQuaternion(data.quat), vsToBtVector(data.pos)) 148 | transa = btTransform() 149 | transb = btTransform() 150 | bodya.motion.getWorldTransform(transa) 151 | bodyb.motion.getWorldTransform(transb) 152 | framea = transa.inverse() * jointTransform 153 | frameb = transb.inverse() * jointTransform 154 | 155 | if (data.constype == "point"): 156 | self.cons = btPoint2PointConstraint(bodya.body, bodyb.body, framea.getOrigin(), frameb.getOrigin()) 157 | elif (data.constype == "cone"): 158 | self.cons = btConeTwistConstraint(bodya.body, bodyb.body, framea, frameb) 159 | self.cons.setLimit(degToRad(data.rotx), degToRad(data.roty), degToRad(data.twist)) 160 | #end 161 | #end 162 | #end 163 | 164 | class Softbody: 165 | def __init__(self, data, worldinfo): 166 | nnodes = len(data.nodelist) 167 | posdata = btVector3Array(nnodes) 168 | massdata = floatArray(nnodes) 169 | 170 | counter = 0 171 | for node in data.nodelist: 172 | posdata[counter] = vsToBtVector(node[2]) 173 | massdata[counter] = 20.0*node[1]/float(nnodes) 174 | counter+=1 175 | #end 176 | 177 | self.body = btSoftBody(worldinfo, nnodes, posdata, massdata) 178 | btSoftBodySetStretch(self.body, 1-data.stretch) 179 | btSoftBodySetShear(self.body, 1-data.shear) 180 | 181 | del posdata 182 | del massdata 183 | 184 | for link in data.linklist: 185 | self.body.appendLink(link[0], link[1]) 186 | #end 187 | 188 | for face in data.facelist: 189 | self.body.appendTetra(face[0], face[1], face[2], face[3]) 190 | #end 191 | 192 | bend = self.body.appendMaterial() 193 | bend.m_kLST = 1 - data.bend 194 | self.body.generateBendingConstraints(2, bend) 195 | 196 | self.body.m_cfg.kDP = data.damp 197 | self.body.m_cfg.kDF = data.friction 198 | self.body.m_cfg.kSRHR_CL = 1.0 199 | #self.body.m_cfg.piterations = 20 200 | #self.body.m_cfg.citerations = 20 201 | self.body.m_cfg.collisions = fCollision.CL_SS+fCollision.CL_RS+fCollision.CL_SELF; 202 | 203 | self.body.randomizeConstraints() 204 | self.body.setActivationState(DISABLE_DEACTIVATION) 205 | self.body.getCollisionShape().setMargin(data.margin) 206 | self.body.generateClusters(0) 207 | #end 208 | 209 | def setPosition(self, n, pos): 210 | btSoftBodySetNodePosition(self.body, n, vsToBtVector(pos)) 211 | #end 212 | 213 | def getPosition(self, n): 214 | return btToVsVector(btSoftBodyGetNodePosition(self.body, n)) 215 | #end 216 | #end 217 | -------------------------------------------------------------------------------- /sdktools/python/2.7/win32/Lib/site-packages/sfmphys/dagutils.py: -------------------------------------------------------------------------------- 1 | import vs 2 | import sfm 3 | import sfmUtils 4 | 5 | from sfmphys import sessionutils 6 | 7 | def TransformToPosQuat(transform): 8 | pos = vs.Vector() 9 | quat = vs.Quaternion() 10 | vs.MatrixPosition(transform, pos) 11 | vs.MatrixQuaternion(transform, quat) 12 | 13 | return pos, quat 14 | #end 15 | 16 | def TransformToPosEuler(transform): 17 | pos = vs.Vector() 18 | quat = vs.Quaternion() 19 | vs.MatrixPosition(transform, pos) 20 | vs.MatrixQuaternion(transform, quat) 21 | 22 | euler = vs.RadianEuler() 23 | vs.QuaternionAngles(quat, euler) 24 | 25 | return pos, euler 26 | #end 27 | 28 | def PosQuatToTransform(pos, rot): 29 | result = vs.matrix3x4_t() 30 | vs.QuaternionMatrix(rot, pos, result) 31 | return result 32 | #end 33 | 34 | def GetAbsTransformAtTime(dag, time): 35 | trans = GetTransformAtTime(dag, time) 36 | 37 | if (dag.GetType() == vs.CDmeGameModel_GetStaticTypeSymbol()): 38 | return trans 39 | else: 40 | partrans = GetAbsTransformAtTime(dag.GetParent(), time) 41 | 42 | result = vs.matrix3x4_t() 43 | vs.ConcatTransforms(partrans, trans, result) 44 | return result 45 | #end 46 | #end 47 | 48 | def GetRelativeTransformAtTime(parent, child, time): 49 | partrans = GetAbsTransformAtTime(parent, time) 50 | childtrans = GetAbsTransformAtTime(child, time) 51 | 52 | inv_partrans = vs.matrix3x4_t() 53 | vs.MatrixInvert(partrans, inv_partrans) 54 | 55 | result = vs.matrix3x4_t() 56 | vs.ConcatTransforms(inv_partrans, childtrans, result) 57 | 58 | return result 59 | #end 60 | 61 | def GetTransformAtTime(dag, time): 62 | pos = GetValueAtTime(dag.FindTransformControl().GetPositionChannel(), time) 63 | quat = GetValueAtTime(dag.FindTransformControl().GetOrientationChannel(), time) 64 | 65 | return PosQuatToTransform(pos, quat) 66 | #end 67 | 68 | def GetValueAtTime(channel, time): 69 | clipstart = sessionutils.getCurrentShot().GetStartTime() 70 | start = channel.FindOwnerClipForChannel(sessionutils.getCurrentShot()).GetStartTime() 71 | return channel.GetLog().GetValue((time - clipstart) - start) 72 | #end 73 | 74 | def SetAbsTransformAtTime(dag, time, transform): 75 | if (dag.GetType() == vs.CDmeGameModel_GetStaticTypeSymbol()): 76 | SetTransformAtTime(dag, time, transform) 77 | else: 78 | partrans = GetAbsTransformAtTime(dag.GetParent(), time) 79 | 80 | inv_partrans = vs.matrix3x4_t() 81 | vs.MatrixInvert(partrans, inv_partrans) 82 | 83 | result = vs.matrix3x4_t() 84 | vs.ConcatTransforms(inv_partrans, transform, result) 85 | 86 | SetTransformAtTime(dag, time, result) 87 | #end 88 | #end 89 | 90 | def SetRelativeTransformAtTime(parent, child, time, transform): 91 | partrans = GetAbsTransformAtTime(parent, time) 92 | 93 | result = vs.matrix3x4_t() 94 | vs.ConcatTransforms(partrans, transform) 95 | 96 | SetAbsTransformAtTime(child, time, result) 97 | #end 98 | 99 | def SetTransformAtTime(dag, time, transform): 100 | pos = vs.Vector() 101 | quat = vs.Quaternion() 102 | vs.MatrixPosition(transform, pos) 103 | vs.MatrixQuaternion(transform, quat) 104 | SetValueAtTime(dag.FindTransformControl().GetPositionChannel(), time, pos) 105 | SetValueAtTime(dag.FindTransformControl().GetOrientationChannel(), time, quat) 106 | #end 107 | 108 | def SetValueAtTime(channel, time, value): 109 | clipstart = sessionutils.getCurrentShot().GetStartTime() 110 | start = channel.FindOwnerClipForChannel(sessionutils.getCurrentShot()).GetStartTime() 111 | channel.GetLog().SetKey((time - clipstart) - start, value); 112 | #end -------------------------------------------------------------------------------- /sdktools/python/2.7/win32/Lib/site-packages/sfmphys/rigutils.py: -------------------------------------------------------------------------------- 1 | import vs 2 | import sfm 3 | import sys 4 | import sfmUtils 5 | from .dagutils import * 6 | 7 | #configuration for animset attribute names 8 | 9 | #rigidbody properties 10 | name_targetname = "Target" 11 | name_shape = "Shape" 12 | name_boxsize = "BoxSize" 13 | name_boxcenter = "BoxCenter" 14 | name_margin = "Margin" 15 | name_mass = "Mass" 16 | name_bounce = "Bounce" 17 | name_friction = "Friction" 18 | name_kinematic = "Kinematic" 19 | name_lindamp = "LinearDamping" 20 | name_rotdamp = "RotationalDamping" 21 | name_handle = "Handle" 22 | name_force = "Force" 23 | 24 | #constraint properties 25 | name_typename = "Type" 26 | name_bodya = "BodyA" 27 | name_bodyb = "BodyB" 28 | name_rotx = "MaxRotationX" 29 | name_roty = "MaxRotationY" 30 | name_twist = "MaxTwist" 31 | 32 | #softbody properties 33 | name_boneprefix = "BonePrefix" 34 | name_nodelist = "NodeList" 35 | name_linklist = "LinkList" 36 | name_facelist = "FaceList" 37 | name_stretch = "Stretch" 38 | name_shear = "Shear" 39 | name_bend = "Bend" 40 | name_nodemass = "Mass" 41 | name_nodename = "Name" 42 | 43 | def groupAddStringAttribute(group, name, value): 44 | attribute = group.AddAttribute(name, vs.AT_STRING) 45 | attribute.SetValue(value) 46 | #end 47 | 48 | def groupCreateControlledValue(group, name, value, animset, shot): 49 | tempControl, tempValue = sfmUtils.CreateControlledValue(name, "value", vs.AT_FLOAT, value, animset, shot) 50 | group.AddControl(tempControl) 51 | #end 52 | 53 | def groupGetControlledValueAtTime(group, name, time): 54 | return GetValueAtTime(group.FindControlByName(name, False).channel, time) 55 | #end 56 | 57 | ################################################################################# 58 | ##Rigidbody 59 | ################################################################################# 60 | class RigidbodyRig: 61 | def __init__(self, data=None, group=None, time=None): 62 | self.setDefaults() 63 | 64 | if (data is not None): 65 | self.readFromData(data) 66 | elif (group is not None) and (time is not None): 67 | self.readFromGroup(group, time) 68 | #end 69 | #end 70 | 71 | def setDefaults(self): 72 | self.target = "ERROR" 73 | self.shape = "box" 74 | self.boxsize = vs.Vector(0,0,0) 75 | self.boxcenter = vs.Vector(0,0,0) 76 | self.margin = 0.04 77 | self.bounce = 0.25 78 | self.friction = 0.9 79 | self.mass = 1 80 | self.lindamp = 0.05 81 | self.rotdamp = 0.1 82 | #end 83 | 84 | def createSuffix(self, target): 85 | return " ("+target+")" 86 | #end 87 | 88 | def readFromData(self, data): 89 | for key in data: 90 | setattr(self, key, data[key]) 91 | #end 92 | 93 | def writeToGroup(self, group): 94 | animset = sfm.GetCurrentAnimationSet() 95 | shot = sfm.GetCurrentShot() 96 | rootDag = sfmUtils.FindFirstDag(["RootTransform", "rootTransform", "roottransform", "Roottransform"]) 97 | suffix = self.createSuffix(self.target) 98 | 99 | groupAddStringAttribute(group, name_targetname, self.target) 100 | 101 | group.SetValue(name_boxsize, self.boxsize) 102 | group.SetValue(name_boxcenter, self.boxcenter) 103 | group.SetValue(name_margin, float(self.margin)) 104 | group.SetValue(name_mass, float(self.mass)) 105 | 106 | self.handle = sfm.CreateRigHandle(name_handle+suffix, group=group.GetName()) 107 | 108 | #position the handle at the box center 109 | bonedag = sfm.FindDag(self.target) 110 | CenterTransform = PosQuatToTransform(self.boxcenter, vs.Quaternion(0,0,0,1)) 111 | ResultTransform = vs.matrix3x4_t() 112 | vs.ConcatTransforms(bonedag.GetAbsTransform(), CenterTransform, ResultTransform) 113 | self.handle.SetAbsTransform(ResultTransform) 114 | sfmUtils.Parent(self.handle, rootDag) 115 | sfm.ParentConstraint(name_handle+suffix, self.target, mo=True) 116 | 117 | self.force = sfm.CreateRigHandle(name_force+suffix, group=group.GetName()) 118 | self.force.SetAbsTransform(self.handle.GetAbsTransform()) 119 | sfmUtils.Parent(self.force, self.handle) 120 | 121 | shapeval = 0 122 | if self.shape == "sphere": 123 | shapeval = 1 124 | groupCreateControlledValue(group, name_shape+suffix, shapeval, animset, shot) 125 | 126 | kineval = 0 127 | if self.mass == 0: 128 | kineval = 1 129 | groupCreateControlledValue(group, name_kinematic+suffix, kineval, animset, shot) 130 | 131 | groupCreateControlledValue(group, name_bounce+suffix, self.bounce, animset, shot) 132 | groupCreateControlledValue(group, name_friction+suffix, self.friction, animset, shot) 133 | groupCreateControlledValue(group, name_lindamp+suffix, self.lindamp, animset, shot) 134 | groupCreateControlledValue(group, name_rotdamp+suffix, self.rotdamp, animset, shot) 135 | #end 136 | 137 | def readFromGroup(self, group, time): 138 | self.target = group.GetValue(name_targetname) 139 | suffix = self.createSuffix(self.target) 140 | 141 | self.handle = group.FindControlByName(name_handle+suffix, False).GetDag() 142 | self.pos, self.quat = TransformToPosQuat(GetAbsTransformAtTime(self.handle, time)) 143 | 144 | self.force = group.FindControlByName(name_force+suffix, False).GetDag() 145 | 146 | self.boxsize = group.GetValue(name_boxsize) 147 | self.boxcenter = group.GetValue(name_boxcenter) 148 | self.margin = group.GetValue(name_margin) 149 | 150 | shapeval = groupGetControlledValueAtTime(group, name_shape+suffix, time) 151 | self.shape = "box" 152 | if shapeval == 1: 153 | self.shape = "sphere" 154 | 155 | self.mass = group.GetValue(name_mass) 156 | kineval = groupGetControlledValueAtTime(group, name_kinematic+suffix, time) 157 | if kineval == 1: 158 | self.mass = 0 159 | 160 | self.bounce = groupGetControlledValueAtTime(group, name_bounce+suffix, time) 161 | self.friction = groupGetControlledValueAtTime(group, name_friction+suffix, time) 162 | self.lindamp = groupGetControlledValueAtTime(group, name_lindamp+suffix, time) 163 | self.rotdamp = groupGetControlledValueAtTime(group, name_rotdamp+suffix, time) 164 | #end 165 | #end 166 | 167 | ################################################################################# 168 | ##Constraint 169 | ################################################################################# 170 | class ConstraintRig: 171 | def __init__(self, data=None, group=None, time=None): 172 | self.setDefaults() 173 | 174 | if (data is not None): 175 | self.readFromData(data) 176 | elif (group is not None) and (time is not None): 177 | self.readFromGroup(group, time) 178 | #end 179 | #end 180 | 181 | def setDefaults(self): 182 | self.constype = "point" 183 | self.pos = None 184 | self.quat = vs.Quaternion(0,0,0,1) 185 | self.bodya = "" 186 | self.bodyb = "" 187 | #end 188 | 189 | def readFromData(self, data): 190 | for key in data: 191 | setattr(self, key, data[key]) 192 | #end 193 | 194 | def createSuffix(self, constype, bodya, bodyb): 195 | return " (" + constype + "_constraint "+bodya+" -> "+bodyb+")" 196 | #end 197 | 198 | def writeToGroup(self, group): 199 | animset = sfm.GetCurrentAnimationSet() 200 | shot = sfm.GetCurrentShot() 201 | rootDag = sfmUtils.FindFirstDag(["RootTransform", "rootTransform", "roottransform", "Roottransform"]) 202 | suffix = self.createSuffix(self.constype, self.bodya, self.bodyb) 203 | 204 | groupAddStringAttribute(group, name_typename, self.constype) 205 | groupAddStringAttribute(group, name_bodya, self.bodya) 206 | groupAddStringAttribute(group, name_bodyb, self.bodyb) 207 | 208 | self.handle = sfm.CreateRigHandle(name_handle+suffix, group=group.GetName()) 209 | 210 | #position the handle at bodya plus some offset 211 | daga = sfm.FindDag(self.bodya) 212 | dagb = sfm.FindDag(self.bodyb) 213 | ResultTransform = vs.matrix3x4_t() 214 | if (self.pos is None): 215 | #placing pos at the same spot as bodyb is usually a good guess 216 | ResultTransform = PosQuatToTransform(dagb.GetAbsPosition(), daga.GetAbsOrientation()) 217 | else: 218 | OffsetTransform = PosQuatToTransform(self.pos, vs.Quaternion(0,0,0,1)) 219 | vs.ConcatTransforms(daga.GetAbsTransform(), OffsetTransform, ResultTransform) 220 | #end 221 | self.handle.SetAbsTransform(ResultTransform) 222 | sfmUtils.Parent(self.handle, daga) 223 | 224 | if (self.constype == "cone"): 225 | groupCreateControlledValue(group, name_rotx+suffix, self.rotx / 180.0, animset, shot) 226 | groupCreateControlledValue(group, name_roty+suffix, self.roty / 180.0, animset, shot) 227 | groupCreateControlledValue(group, name_twist+suffix, self.twist / 180.0, animset, shot) 228 | #end 229 | #end 230 | 231 | def readFromGroup(self, group, time): 232 | self.constype = group.GetValue(name_typename) 233 | self.bodya = group.GetValue(name_bodya) 234 | self.bodyb = group.GetValue(name_bodyb) 235 | suffix = self.createSuffix(self.constype, self.bodya, self.bodyb) 236 | 237 | self.handle = group.FindControlByName(name_handle+suffix, False).GetDag() 238 | self.pos, self.quat = TransformToPosQuat(GetAbsTransformAtTime(self.handle, time)) 239 | 240 | if (self.constype == "cone"): 241 | self.rotx = groupGetControlledValueAtTime(group, name_rotx+suffix, time) * 180.0 242 | self.roty = groupGetControlledValueAtTime(group, name_roty+suffix, time) * 180.0 243 | self.twist = groupGetControlledValueAtTime(group, name_twist+suffix, time) * 180.0 244 | #end 245 | #end 246 | #end 247 | 248 | ################################################################################# 249 | ##Softbody 250 | ################################################################################# 251 | class SoftbodyRig: 252 | def __init__(self, data=None, group=None, time=None): 253 | self.setDefaults() 254 | 255 | if (data is not None): 256 | self.readFromData(data) 257 | elif (group is not None) and (time is not None): 258 | self.readFromGroup(group, time) 259 | #end 260 | #end 261 | 262 | def setDefaults(self): 263 | self.boneprefix = "" 264 | self.nodelist = list() 265 | self.linklist = list() 266 | self.facelist = list() 267 | self.margin = 0.04 268 | self.friction = 0.9 269 | self.damp = 0.05 270 | self.stretch = 0 271 | self.shear = 0.1 272 | self.bend = 0.9 273 | self.mass = 1.0 274 | #end 275 | 276 | def createSuffix(self, animset, boneprefix): 277 | return " ("+animset+"_"+boneprefix+")" 278 | #end 279 | 280 | def readFromData(self, data): 281 | for key in data: 282 | setattr(self, key, data[key]) 283 | #end 284 | 285 | def writeToGroup(self, group): 286 | animset = sfm.GetCurrentAnimationSet() 287 | shot = sfm.GetCurrentShot() 288 | suffix = self.createSuffix(animset.GetName(), self.boneprefix) 289 | 290 | attrNodelist = group.AddAttribute(name_nodelist, vs.AT_ELEMENT_ARRAY) 291 | attrLinklist = group.AddAttribute(name_linklist, vs.AT_ELEMENT_ARRAY) 292 | attrFacelist = group.AddAttribute(name_facelist, vs.AT_ELEMENT_ARRAY) 293 | 294 | counter=0 295 | for bone in self.nodelist: 296 | temp = vs.CreateElement("Bone "+str(counter), group.GetFileId()) 297 | temp.AddAttribute(name_nodename, vs.AT_STRING).SetValue(bone[0]) 298 | temp.SetValue(name_nodemass, bone[1]) 299 | attrNodelist.append(temp) 300 | counter+=1 301 | #end 302 | counter=0 303 | for link in self.linklist: 304 | temp = vs.CreateElement("Link "+str(counter), group.GetFileId()) 305 | temp.SetValue("a", link[0]) 306 | temp.SetValue("b", link[1]) 307 | attrLinklist.append(temp) 308 | counter+=1 309 | #end 310 | counter=0 311 | for face in self.facelist: 312 | temp = vs.CreateElement("Link "+str(counter), group.GetFileId()) 313 | temp.SetValue("a", face[0]) 314 | temp.SetValue("b", face[1]) 315 | temp.SetValue("c", face[2]) 316 | temp.SetValue("d", face[3]) 317 | attrFacelist.append(temp) 318 | counter+=1 319 | #end 320 | 321 | groupAddStringAttribute(group, name_boneprefix, self.boneprefix) 322 | group.SetValue(name_margin, float(self.margin)) 323 | group.SetValue(name_mass, float(self.mass)) 324 | groupCreateControlledValue(group, name_stretch+suffix, self.stretch, animset, shot) 325 | groupCreateControlledValue(group, name_shear+suffix, self.shear, animset, shot) 326 | groupCreateControlledValue(group, name_bend+suffix, self.bend, animset, shot) 327 | groupCreateControlledValue(group, name_lindamp+suffix, self.damp, animset, shot) 328 | groupCreateControlledValue(group, name_friction+suffix, self.friction, animset, shot) 329 | #end 330 | 331 | def readFromGroup(self, group, time): 332 | animset = group.FindAnimationSet(True) 333 | shot = sfm.GetCurrentShot() 334 | self.boneprefix = group.GetValue(name_boneprefix) 335 | suffix = self.createSuffix(animset.GetName(), self.boneprefix) 336 | 337 | self.daglist = list() 338 | 339 | for bone in group.GetValue(name_nodelist): 340 | dag = sfm.FindDag(animset.GetName()+":"+bone.GetValue(name_nodename)) 341 | pos, quat = TransformToPosQuat(GetAbsTransformAtTime(dag, time)) 342 | self.nodelist.append((bone.GetValue(name_nodename), bone.GetValue(name_nodemass), pos)) 343 | self.daglist.append(dag) 344 | #end 345 | for link in group.GetValue(name_linklist): 346 | self.linklist.append((link.GetValue("a"), link.GetValue("b"))) 347 | #end 348 | for face in group.GetValue(name_facelist): 349 | self.facelist.append((face.GetValue("a"), face.GetValue("b"), face.GetValue("c"), face.GetValue("d"))) 350 | #end 351 | 352 | self.margin = group.GetValue(name_margin) 353 | self.mass = group.GetValue(name_mass) 354 | self.stretch = groupGetControlledValueAtTime(group, name_stretch+suffix, time) 355 | self.shear = groupGetControlledValueAtTime(group, name_shear+suffix, time) 356 | self.bend = groupGetControlledValueAtTime(group, name_bend+suffix, time) 357 | self.damp = groupGetControlledValueAtTime(group, name_lindamp+suffix, time) 358 | self.friction = groupGetControlledValueAtTime(group, name_friction+suffix, time) 359 | #end 360 | #end -------------------------------------------------------------------------------- /sdktools/python/2.7/win32/Lib/site-packages/sfmphys/sessionutils.py: -------------------------------------------------------------------------------- 1 | import vs 2 | import sfm 3 | import sfmUtils 4 | 5 | g_shot = None 6 | def setCurrentShot(shot): 7 | global g_shot 8 | g_shot = shot 9 | #end 10 | 11 | def getCurrentShot(): 12 | global g_shot 13 | return g_shot 14 | #end 15 | 16 | def FindElementByName(name): 17 | max = vs.g_pDataModel.GetElementsAllocatedSoFar() 18 | elementHandle = vs.g_pDataModel.FirstAllocatedElement() 19 | 20 | for i in range(max): 21 | element = vs.g_pDataModel.GetElementName(elementHandle) 22 | if (element == None): 23 | continue 24 | 25 | if (element.GetName() == name): 26 | return vs.g_pDataModel.GetElement(elementHandle) 27 | #end if 28 | elementHandle = vs.g_pDataModel.NextAllocatedElement(elementHandle) 29 | #end for 30 | 31 | return None 32 | #end 33 | 34 | def FindElementsOfType(type): 35 | max = vs.g_pDataModel.GetElementsAllocatedSoFar() 36 | elementHandle = vs.g_pDataModel.FirstAllocatedElement() 37 | 38 | ret = [] 39 | 40 | for i in range(max): 41 | element = vs.g_pDataModel.GetElement(elementHandle) 42 | if (element == None): 43 | continue 44 | 45 | if (element.GetTypeString() == type): 46 | ret.append(element) 47 | #end if 48 | elementHandle = vs.g_pDataModel.NextAllocatedElement(elementHandle) 49 | #end for 50 | 51 | return ret 52 | #end 53 | 54 | def FindElementByTypeAndName(type, name): 55 | max = vs.g_pDataModel.GetElementsAllocatedSoFar() 56 | elementHandle = vs.g_pDataModel.FirstAllocatedElement() 57 | 58 | for i in range(max): 59 | element = vs.g_pDataModel.GetElement(elementHandle) 60 | if (element == None): 61 | continue 62 | 63 | if (element.GetName() == name and element.GetTypeString() == type): 64 | return element 65 | #end if 66 | elementHandle = vs.g_pDataModel.NextAllocatedElement(elementHandle) 67 | #end for 68 | 69 | return None 70 | #end 71 | 72 | def GetAnimationSets(): 73 | return getCurrentShot().animationSets 74 | #end 75 | 76 | def GetCurrentTimeSelection(): 77 | #timeSelection has attributes "falloff_left", "hold_left", "falloff_right", "hold_right" 78 | timeSelection = FindElementByTypeAndName("DmeTimeSelection", "timeSelection") 79 | return timeSelection 80 | #end 81 | 82 | def GetFrameRate(): 83 | renderSettings = FindElementByTypeAndName("DmElement", "renderSettings") 84 | return renderSettings.GetValue("frameRate") 85 | #end -------------------------------------------------------------------------------- /sdktools/python/2.7/win32/Lib/site-packages/swigbullet/___init__.pyd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimPiko/sfmphys/95edba974fd7e7b0e93af78aadf7b4a5f762c2b1/sdktools/python/2.7/win32/Lib/site-packages/swigbullet/___init__.pyd -------------------------------------------------------------------------------- /usermod/materials/lambert.vtf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimPiko/sfmphys/95edba974fd7e7b0e93af78aadf7b4a5f762c2b1/usermod/materials/lambert.vtf -------------------------------------------------------------------------------- /usermod/materials/lambert1.vmt: -------------------------------------------------------------------------------- 1 | "vertexlitgeneric" 2 | { 3 | "$basetexture" "lambert" 4 | $nocull 1 5 | } 6 | -------------------------------------------------------------------------------- /usermod/models/narry/cloth_test.dx80.vtx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimPiko/sfmphys/95edba974fd7e7b0e93af78aadf7b4a5f762c2b1/usermod/models/narry/cloth_test.dx80.vtx -------------------------------------------------------------------------------- /usermod/models/narry/cloth_test.dx90.vtx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimPiko/sfmphys/95edba974fd7e7b0e93af78aadf7b4a5f762c2b1/usermod/models/narry/cloth_test.dx90.vtx -------------------------------------------------------------------------------- /usermod/models/narry/cloth_test.mdl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimPiko/sfmphys/95edba974fd7e7b0e93af78aadf7b4a5f762c2b1/usermod/models/narry/cloth_test.mdl -------------------------------------------------------------------------------- /usermod/models/narry/cloth_test.physics.txt: -------------------------------------------------------------------------------- 1 | { 2 | "cloths": [ 3 | { 4 | "width": 14, 5 | "height": 14, 6 | "boneformat": "rc_R0C{0}", 7 | "formatranges": [ 8 | [1, 196] 9 | ], 10 | "stretch": 0, 11 | "shear": 0.6, 12 | "bend": 0.9 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /usermod/models/narry/cloth_test.sw.vtx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimPiko/sfmphys/95edba974fd7e7b0e93af78aadf7b4a5f762c2b1/usermod/models/narry/cloth_test.sw.vtx -------------------------------------------------------------------------------- /usermod/models/narry/cloth_test.vvd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimPiko/sfmphys/95edba974fd7e7b0e93af78aadf7b4a5f762c2b1/usermod/models/narry/cloth_test.vvd -------------------------------------------------------------------------------- /windrunner_cape.physics.txt: -------------------------------------------------------------------------------- 1 | { 2 | "cloths": [ 3 | { 4 | "width": 3, 5 | "height": 3, 6 | "boneformat": "cape_R{0}C{1}", 7 | "formatranges": [ 8 | [0, 2], 9 | [0, 2] 10 | ], 11 | "stretch": 0, 12 | "shear": 0.6, 13 | "bend": 0.9 14 | } 15 | ] 16 | } --------------------------------------------------------------------------------