rlm@158: #+title: Movement! rlm@158: #+author: Robert McIntyre rlm@158: #+email: rlm@mit.edu rlm@158: #+description: muscles for a simulated creature rlm@158: #+keywords: simulation, jMonkeyEngine3, clojure rlm@158: #+SETUPFILE: ../../aurellem/org/setup.org rlm@158: #+INCLUDE: ../../aurellem/org/level-0.org rlm@158: rlm@180: rlm@180: Surprisingly enough, terristerial creatures only move by using torque rlm@180: applied about their joints. There's not a single straight line of rlm@180: force in the human body at all! (A straight line of force would rlm@180: correspond to some sort of jet or rocket propulseion.) rlm@180: rlm@180: rlm@180: Here's how motor-control/ proprioception will work: Each muscle is rlm@180: defined by a 1-D array of numbers (the "motor pool") each of which rlm@180: represent muscle fibers. A muscle also has a scalar :strength factor rlm@180: which determines how strong the muscle as a whole is. The effector rlm@180: function for a muscle takes a number < (count motor-pool) and that rlm@180: number is said to "activate" all the muscle fibers whose index is rlm@180: lower than the number. Each fiber will apply force in proportion to rlm@180: its value in the array. Lower values cause less force. The lower rlm@180: values can be put at the "beginning" of the 1-D array to simulate the rlm@180: layout of actual human muscles, which are capable of more percise rlm@180: movements when exerting less force. rlm@180: rlm@158: #+name: movement rlm@158: #+begin_src clojure rlm@158: (ns cortex.movement rlm@180: "Give simulated creatures defined in special blender files the power rlm@180: to move around in a simulated environment." rlm@180: {:author "Robert McIntyre"} rlm@158: (:use (cortex world util sense body)) rlm@180: (:use clojure.contrib.def) rlm@180: (:import java.awt.image.BufferedImage) rlm@180: (:import com.jme3.scene.Node) rlm@180: (:import com.jme3.math.Vector3f) rlm@180: (:import com.jme3.bullet.control.RigidBodyControl)) rlm@158: rlm@180: (defn muscle-profile rlm@180: "Return a vector where each entry is the strength of the \"motor rlm@180: pool\" at that part in the muscle." rlm@180: [#^BufferedImage profile] rlm@158: (vec rlm@180: (let [width (.getWidth profile)] rlm@158: (for [x (range width)] rlm@158: (- 255 rlm@158: (bit-and rlm@158: 0x0000FF rlm@180: (.getRGB profile x 0))))))) rlm@158: rlm@180: (defvar rlm@180: ^{:arglists '([creature])} rlm@180: muscles rlm@180: (sense-nodes "muscles") rlm@180: "Return the children of the creature's \"muscles\" node.") rlm@158: rlm@180: (defn movement-fn rlm@180: "Returns a function which when called with a integer value inside a rlm@191: running simulation will cause movement in the creature according rlm@191: to the muscle's position and strength profile. Each function rlm@191: returns the amount of force applied / max force." rlm@180: [#^Node parts #^Node muscle] rlm@158: (let [target (closest-node parts muscle) rlm@158: axis rlm@158: (.mult (.getWorldRotation muscle) Vector3f/UNIT_Y) rlm@158: strength (meta-data muscle "strength") rlm@158: image-name (read-string (meta-data muscle "muscle")) rlm@180: image (load-image image-name) rlm@180: fibers (muscle-profile image) rlm@158: fiber-integral (reductions + fibers) rlm@191: force-index rlm@191: (vec (map #(float (* strength (/ % (last fiber-integral)))) rlm@191: fiber-integral)) rlm@158: control (.getControl target RigidBodyControl)] rlm@158: (fn [n] rlm@191: (let [pool-index (max 0 (min n (dec (count fibers)))) rlm@191: force (force-index pool-index)] rlm@191: (.applyTorque control (.mult axis force)) rlm@191: (float (/ force strength)))))) rlm@191: rlm@158: rlm@180: (defn movement! rlm@180: "Endow the creature with the power of movement. Returns a sequence rlm@180: of functions, each of which accept an integer value and will rlm@180: activate their corresponding muscle." rlm@158: [#^Node creature] rlm@180: (for [muscle (muscles creature)] rlm@180: (movement-fn creature muscle))) rlm@158: rlm@191: (defn movement-display-kernel rlm@191: "Display muscle exertion data as a bar filling up with red." rlm@191: [exertion] rlm@191: (let [height 20 rlm@191: width 300 rlm@191: image (BufferedImage. width height rlm@191: BufferedImage/TYPE_INT_RGB) rlm@191: fill (min (int (* width exertion)) width)] rlm@191: (dorun rlm@191: (for [x (range fill) rlm@191: y (range height)] rlm@191: (.setRGB image x y 0xFF0000))) rlm@191: image)) rlm@191: rlm@191: (defn view-movement rlm@191: "Creates a function which accepts a list of muscle-exertion data and rlm@191: displays each element of the list to the screen." rlm@191: [] rlm@191: (view-sense movement-display-kernel)) rlm@191: rlm@158: #+end_src rlm@158: rlm@158: rlm@158: rlm@158: rlm@158: rlm@158: * COMMENT code generation rlm@158: #+begin_src clojure :tangle ../src/cortex/movement.clj rlm@158: <> rlm@158: #+end_src