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@158: #+name: movement rlm@158: #+begin_src clojure rlm@158: (ns cortex.movement rlm@158: (:use (cortex world util sense body)) rlm@158: (:import jme3tools.converters.ImageToAwt) rlm@158: (:import java.awt.image.BufferedImage)) rlm@158: rlm@158: (cortex.import/mega-import-jme3) rlm@158: rlm@158: rlm@158: rlm@158: rlm@158: ;; here's how motor-control/ proprioception will work: Each muscle is rlm@158: ;; defined by a 1-D array of numbers (the "motor pool") each of which rlm@158: ;; represent muscle fibers. A muscle also has a scalar :strength rlm@158: ;; factor which determines how strong the muscle as a whole is. rlm@158: ;; The effector function for a muscle takes a number < (count rlm@158: ;; motor-pool) and that number is said to "activate" all the muscle rlm@158: ;; fibers whose index is lower than the number. Each fiber will apply rlm@158: ;; force in proportion to its value in the array. Lower values cause rlm@158: ;; less force. The lower values can be put at the "beginning" of the rlm@158: ;; 1-D array to simulate the layout of actual human muscles, which are rlm@158: ;; capable of more percise movements when exerting less force. rlm@158: rlm@158: ;; I don't know how to encode proprioception, so for now, just return rlm@158: ;; a function for each joint that returns a triplet of floats which rlm@158: ;; represent relative roll, pitch, and yaw. Write display code for rlm@158: ;; this though. rlm@158: rlm@158: (defn muscle-fiber-values rlm@158: "get motor pool strengths" rlm@158: [#^BufferedImage image] rlm@158: (vec rlm@158: (let [width (.getWidth image)] rlm@158: (for [x (range width)] rlm@158: (- 255 rlm@158: (bit-and rlm@158: 0x0000FF rlm@158: (.getRGB image x 0))))))) rlm@158: rlm@158: rlm@158: (defn creature-muscles rlm@158: "Return the children of the creature's \"muscles\" node." rlm@158: [#^Node creature] rlm@158: (if-let [muscle-node (.getChild creature "muscles")] rlm@158: (seq (.getChildren muscle-node)) rlm@158: (do (println-repl "could not find muscles node") []))) rlm@158: rlm@158: (defn single-muscle [#^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@158: image rlm@158: (ImageToAwt/convert rlm@158: (.getImage (.loadTexture (asset-manager) image-name)) rlm@158: false false 0) rlm@158: fibers (muscle-fiber-values image) rlm@158: fiber-integral (reductions + fibers) rlm@158: force-index (vec rlm@158: (map rlm@158: #(float (* strength (/ % (last rlm@158: fiber-integral)))) rlm@158: fiber-integral)) rlm@158: control (.getControl target RigidBodyControl)] rlm@158: (fn [n] rlm@158: (let [pool-index (min n (count fibers))] rlm@158: (.applyTorque control (.mult axis (force-index n))))))) rlm@158: rlm@158: rlm@158: (defn enable-muscles rlm@158: "Must be called on a creature after RigidBodyControls have been rlm@158: created." rlm@158: [#^Node creature] rlm@158: (let [muscles (creature-muscles creature)] rlm@158: (for [muscle muscles] rlm@158: (single-muscle creature muscle)))) rlm@158: rlm@158: 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