1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 true true true false false 2 5 1 0 0 0 0.69999999999999996 0 0 0 0.69999999999999996 0.40000000000000002 0.40000000000000002 0.40000000000000002 $K3D_SHARE_PATH 12 4 0.80000000000000004 0.80000000000000004 0.80000000000000004 false 0 100 true false true false true true true 4 0 10 30 5.333333333333324 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 true 6 -0.91059029341276077 5.4677059062392797 0.56341202302314231 false false -0.6666666666666666 0.6666666666666666 0.5 -0.5 1 1000 0 1 0 1 false 0 0 0 0.69999999999999996 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0.81539779770507015 0.31766943161207511 0.48395512572653143 -22.420106289149949 0.57890105501520872 -0.44744598872411051 -0.68166388761396302 35.764441109399627 3.9948377722129336e-15 0.8359893655986087 -0.54874564290391936 24.95256001667796 0 0 0 1 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 $K3D_SHARE_PATH 1 0.5 0.5 0.1 1 1 1 26 0 0 0 0 false 1 1 1 1 1 1 1 $K3D_SHARE_PATH 3000 1 1 1 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 true 28 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 1 0 0 -20 0 1 0 20 0 0 1 30 0 0 0 1 27 35 36 29 32 320 240 1 0 0 0 0 16 16 256 10 1024 false 3 3 gaussian 2 2 1 1 false 0.3 1.6 30 1 constant true false false node_selection 37 1 38 1 true 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 true 0 0 0 0.5 0.5 0.5 1 1 1 1 0 0 true 0 0 0 0.5 0.5 0.5 1 1 1 1 0 0 true 0.20000000000000001 0.20000000000000001 0.20000000000000001 0.5 0.5 0.5 0.59999999999999998 0.59999999999999998 0.59999999999999998 1 0 0 true 0 0 0 0.5 0.5 0.5 1 1 1 1 0 0 2 true 0 0 0 0.5 0.5 0.5 1 1 1 1 0 0 2 true 0.20000000000000001 0.20000000000000001 0.20000000000000001 0.5 0.5 0.5 0.59999999999999998 0.59999999999999998 0.59999999999999998 1 0 0 2 true true true true true true true true true true false 0 1 1 0 0.59999999999999998 0.59999999999999998 true true false 0 1 1 0 0.59999999999999998 0.59999999999999998 true true true true true true true true 31 1 1 1 5 5 5 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 true true false true 39 8 false 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0.91290506125795134 0.095170458743197808 -0.39692181018415762 22.574830415359941 -0.030755874467384942 0.98571192579706701 0.16560808550513229 -9.2010757200075393 0.40701157097145085 -0.1389767803917826 0.90278846393132528 3.9514541815504023 0 0 0 1 4 5 0 0 #python import k3d import ode # This is a hack to solve a problem with execution load-order ... if not locals().has_key("execution_count"): execution_count = 0 execution_count += 1 if execution_count < 3: raise Exception("Not ready to execute yet!") # Convert a K-3D 4x4 matrix to an ODE position def position(m): return (m[0][3], m[1][3], m[2][3]) # Convert a K-3D 4x4 matrix to an ODE 3x3 rotation matrix def rotation(m): return (m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2]) # Convert an ODE position & rotation into a K-3D 4x4 matrix def matrix(p, r): return k3d.matrix4( k3d.vector4(r[0], r[1], r[2], p[0]), k3d.vector4(r[3], r[4], r[5], p[1]), k3d.vector4(r[6], r[7], r[8], p[2]), k3d.vector4(0, 0, 0, 1)) # Define what constitutes a "physical" node ... def is_physical_node(node): if node.factory().name() != "FrozenTransformation": return False if not node.has_property("mass"): return False return True # Handler that will be called whenever a collision may have happened ... def collision_callback(state, geometry1, geometry2): (world, contact_group) = state contacts = ode.collide(geometry1, geometry2) for contact in contacts: contact.setBounce(0.2) contact.setMu(2) joint = ode.ContactJoint(world, contact_group, contact) joint.attach(geometry1.getBody(), geometry2.getBody()) # Setup internal state ... if not locals().has_key("world"): world = ode.World() if not locals().has_key("space"): space = ode.Space() if not locals().has_key("contact_group"): contact_group = ode.JointGroup() if not locals().has_key("floor"): floor = ode.GeomPlane(space, (0, 0, 1), 0) if not locals().has_key("last_time"): last_time = Node.time if not locals().has_key("physical_nodes"): physical_nodes = {} # Identify "physical" nodes ... for node in Document.nodes(): if not is_physical_node(node): continue if not physical_nodes.has_key(node): body = ode.Body(world) body.setPosition(position(node.output_matrix)) body.setRotation(rotation(node.output_matrix)) if node.has_property("linear_velocity"): body.setLinearVel(node.linear_velocity) else: body.setLinearVel((0, 0, 0)) geometry = ode.GeomBox(space, lengths=(5, 5, 5)) geometry.setBody(body) physical_nodes[node] = body # Handle the next time-step ... delta_time = Node.time - last_time last_time = Node.time # If we're moving forward in time, run the next simulation step ... if delta_time > 0: for node in physical_nodes.keys(): mass = ode.Mass() mass.setSphereTotal(node.mass, 1.0) body = physical_nodes[node] body.setMass(mass) space.collide((world, contact_group), collision_callback) world.setGravity(Node.gravity) world.step(delta_time) contact_group.empty() for node in physical_nodes.keys(): body = physical_nodes[node] resting_transform = node.input_matrix new_transform = matrix(body.getPosition(), body.getRotation()) node.matrix = k3d.inverse(resting_transform) * new_transform # If we're moving backward in time, reset all nodes to their resting state ... else: contact_group.empty() for node in physical_nodes.keys(): node.matrix = k3d.identity3() body = physical_nodes[node] body.setPosition(position(node.output_matrix)) body.setRotation(rotation(node.output_matrix)) if node.has_property("linear_velocity"): body.setLinearVel(node.linear_velocity) else: body.setLinearVel((0, 0, 0)) 0 0 0 -9.8100000000000005 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 -10.284 0 5.899999999999999 31 1 1 1 5 5 5 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 true true false true 39 8 false 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 1 0 0 5.716598139368589 0 1 0 0 0 0 1 2.8894341630745393 0 0 0 1 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0.003141592653599989 0.4447098934096004 0.3857177646919993