rlm@158
|
1 #+title: Movement!
|
rlm@158
|
2 #+author: Robert McIntyre
|
rlm@158
|
3 #+email: rlm@mit.edu
|
rlm@158
|
4 #+description: muscles for a simulated creature
|
rlm@158
|
5 #+keywords: simulation, jMonkeyEngine3, clojure
|
rlm@158
|
6 #+SETUPFILE: ../../aurellem/org/setup.org
|
rlm@158
|
7 #+INCLUDE: ../../aurellem/org/level-0.org
|
rlm@158
|
8
|
rlm@180
|
9
|
rlm@180
|
10 Surprisingly enough, terristerial creatures only move by using torque
|
rlm@180
|
11 applied about their joints. There's not a single straight line of
|
rlm@180
|
12 force in the human body at all! (A straight line of force would
|
rlm@180
|
13 correspond to some sort of jet or rocket propulseion.)
|
rlm@180
|
14
|
rlm@180
|
15
|
rlm@180
|
16 Here's how motor-control/ proprioception will work: Each muscle is
|
rlm@180
|
17 defined by a 1-D array of numbers (the "motor pool") each of which
|
rlm@180
|
18 represent muscle fibers. A muscle also has a scalar :strength factor
|
rlm@180
|
19 which determines how strong the muscle as a whole is. The effector
|
rlm@180
|
20 function for a muscle takes a number < (count motor-pool) and that
|
rlm@180
|
21 number is said to "activate" all the muscle fibers whose index is
|
rlm@180
|
22 lower than the number. Each fiber will apply force in proportion to
|
rlm@180
|
23 its value in the array. Lower values cause less force. The lower
|
rlm@180
|
24 values can be put at the "beginning" of the 1-D array to simulate the
|
rlm@180
|
25 layout of actual human muscles, which are capable of more percise
|
rlm@180
|
26 movements when exerting less force.
|
rlm@180
|
27
|
rlm@158
|
28 #+name: movement
|
rlm@158
|
29 #+begin_src clojure
|
rlm@158
|
30 (ns cortex.movement
|
rlm@180
|
31 "Give simulated creatures defined in special blender files the power
|
rlm@180
|
32 to move around in a simulated environment."
|
rlm@180
|
33 {:author "Robert McIntyre"}
|
rlm@158
|
34 (:use (cortex world util sense body))
|
rlm@180
|
35 (:use clojure.contrib.def)
|
rlm@180
|
36 (:import java.awt.image.BufferedImage)
|
rlm@180
|
37 (:import com.jme3.scene.Node)
|
rlm@180
|
38 (:import com.jme3.math.Vector3f)
|
rlm@180
|
39 (:import com.jme3.bullet.control.RigidBodyControl))
|
rlm@158
|
40
|
rlm@180
|
41 (defn muscle-profile
|
rlm@180
|
42 "Return a vector where each entry is the strength of the \"motor
|
rlm@180
|
43 pool\" at that part in the muscle."
|
rlm@180
|
44 [#^BufferedImage profile]
|
rlm@158
|
45 (vec
|
rlm@180
|
46 (let [width (.getWidth profile)]
|
rlm@158
|
47 (for [x (range width)]
|
rlm@158
|
48 (- 255
|
rlm@158
|
49 (bit-and
|
rlm@158
|
50 0x0000FF
|
rlm@180
|
51 (.getRGB profile x 0)))))))
|
rlm@158
|
52
|
rlm@180
|
53 (defvar
|
rlm@180
|
54 ^{:arglists '([creature])}
|
rlm@180
|
55 muscles
|
rlm@180
|
56 (sense-nodes "muscles")
|
rlm@180
|
57 "Return the children of the creature's \"muscles\" node.")
|
rlm@158
|
58
|
rlm@180
|
59 (defn movement-fn
|
rlm@180
|
60 "Returns a function which when called with a integer value inside a
|
rlm@191
|
61 running simulation will cause movement in the creature according
|
rlm@191
|
62 to the muscle's position and strength profile. Each function
|
rlm@191
|
63 returns the amount of force applied / max force."
|
rlm@180
|
64 [#^Node parts #^Node muscle]
|
rlm@158
|
65 (let [target (closest-node parts muscle)
|
rlm@158
|
66 axis
|
rlm@158
|
67 (.mult (.getWorldRotation muscle) Vector3f/UNIT_Y)
|
rlm@158
|
68 strength (meta-data muscle "strength")
|
rlm@158
|
69 image-name (read-string (meta-data muscle "muscle"))
|
rlm@180
|
70 image (load-image image-name)
|
rlm@180
|
71 fibers (muscle-profile image)
|
rlm@158
|
72 fiber-integral (reductions + fibers)
|
rlm@191
|
73 force-index
|
rlm@191
|
74 (vec (map #(float (* strength (/ % (last fiber-integral))))
|
rlm@191
|
75 fiber-integral))
|
rlm@158
|
76 control (.getControl target RigidBodyControl)]
|
rlm@158
|
77 (fn [n]
|
rlm@191
|
78 (let [pool-index (max 0 (min n (dec (count fibers))))
|
rlm@191
|
79 force (force-index pool-index)]
|
rlm@191
|
80 (.applyTorque control (.mult axis force))
|
rlm@191
|
81 (float (/ force strength))))))
|
rlm@191
|
82
|
rlm@158
|
83
|
rlm@180
|
84 (defn movement!
|
rlm@180
|
85 "Endow the creature with the power of movement. Returns a sequence
|
rlm@180
|
86 of functions, each of which accept an integer value and will
|
rlm@180
|
87 activate their corresponding muscle."
|
rlm@158
|
88 [#^Node creature]
|
rlm@180
|
89 (for [muscle (muscles creature)]
|
rlm@180
|
90 (movement-fn creature muscle)))
|
rlm@158
|
91
|
rlm@191
|
92 (defn movement-display-kernel
|
rlm@191
|
93 "Display muscle exertion data as a bar filling up with red."
|
rlm@191
|
94 [exertion]
|
rlm@191
|
95 (let [height 20
|
rlm@191
|
96 width 300
|
rlm@191
|
97 image (BufferedImage. width height
|
rlm@191
|
98 BufferedImage/TYPE_INT_RGB)
|
rlm@191
|
99 fill (min (int (* width exertion)) width)]
|
rlm@191
|
100 (dorun
|
rlm@191
|
101 (for [x (range fill)
|
rlm@191
|
102 y (range height)]
|
rlm@191
|
103 (.setRGB image x y 0xFF0000)))
|
rlm@191
|
104 image))
|
rlm@191
|
105
|
rlm@191
|
106 (defn view-movement
|
rlm@191
|
107 "Creates a function which accepts a list of muscle-exertion data and
|
rlm@191
|
108 displays each element of the list to the screen."
|
rlm@191
|
109 []
|
rlm@191
|
110 (view-sense movement-display-kernel))
|
rlm@191
|
111
|
rlm@158
|
112 #+end_src
|
rlm@158
|
113
|
rlm@158
|
114
|
rlm@158
|
115
|
rlm@158
|
116
|
rlm@158
|
117
|
rlm@158
|
118 * COMMENT code generation
|
rlm@158
|
119 #+begin_src clojure :tangle ../src/cortex/movement.clj
|
rlm@158
|
120 <<movement>>
|
rlm@158
|
121 #+end_src
|