actionscript 3 - Creating a Spine2d library with a custom engine using Affine Coord Space -
for current project, using custom scripting language (god knows why we're doing that) game development. sparing details, engine interprets , exports either flash or ios.
so project, tasked creating spine library assistance animation. part, isn't difficult, our engine similar enough as3 can translate over.
main issue i'm having rendering. creators of scripting language decided use exclusively affine coordinate space system render positions , such. i've tried wrap head around it, without knowledge how works, i'm struggling figure out. all need able manually set position x , y, , manually set rotation angle. immensely appreciated.
at rate, here code render actual spine library stuff (this based off of skeletonsprite.as class as3 library):
package engine.spine.render; //these have same functionality spine2d library as3 import engine.spine.igbone; import engine.spine.igskeleton; import engine.spine.igskeletondata; import engine.spine.igslot; import engine.spine.atlas.igatlasregion; import engine.spine.attachments.igregionattachment; //this stuff our engine libraries import engine.graphics.*; import engine.gui.*; import engine.math.*; import engine.tween.*; import engine.util.igfsm; import engine.igapplication; public class igspinewidget extends iguiwidget { private var m_skeleton : igskeleton; private var m_lasttime : int; private var m_image : igimage; private var m_wrappers : map<igregionattachment, igspineposition> = new map<igregionattachment, igspineposition>(); // works same way dictionary in as3 public function igspinewidget (skeletondata : igskeletondata, image : igimage) { igbone.ydown = true; m_skeleton = new igskeleton(skeletondata); m_skeleton.updateworldtransform(); m_image = image; } protected override function render(g : iggraphics) : void { m_skeleton.update(igapplication.delta_time) ; var draworder : vector<igslot> = skeleton.draworder; (var : int = 0; < draworder.length; i++) { g.push(); { var slot : igslot = draworder[i]; if (slot.attachment != null && slot.attachment.type != igregionattachment.region()) { continue; } var regionattachment : igregionattachment = igregionattachment(slot.attachment); var wrapper : igspineposition = m_wrappers[regionattachment]; if(regionattachment != null) { if (wrapper == null) { wrapper = new igspineposition(); var region : igatlasregion = igatlasregion(regionattachment.rendererobject); var regionheight : double = region.rotate ? region.width : region.height; var regionwidth : double = region.rotate ? region.height : region.width; wrapper.x = region.x; wrapper.y = region.y; wrapper.height = regionheight; wrapper.width = regionwidth; // rotate , scale using default registration point (top left corner, y-down, cw) instead of image center wrapper.affine.rotate(regionattachment.rotation * math.pi / 180); //this rotates position of drawn object, need able set actual rotation instead of translating rotation wrapper.affine.scale(regionattachment.scalex * (regionattachment.width / region.width), regionattachment.scaley * (regionattachment.height / region.height)); // position using attachment translation, shifted if scale , rotation @ image center var radians : double = -regionattachment.rotation * math.pi / 180; var cos : double = math.cos(radians); var sin : double = math.sin(radians); var shiftx : double = -regionattachment.width / 2 * regionattachment.scalex; var shifty : double = -regionattachment.height / 2 * regionattachment.scaley; if (region.rotate) { wrapper.affine.rotate(90); // again, need += rotation 90 degrees, dont have functionality shiftx += regionheight * (regionattachment.width / region.width); } wrapper.affine.translate(0, 0);//(regionattachment.x + shiftx * cos - shifty * sin, -regionattachment.y + shiftx * sin + shifty * cos); m_wrappers[regionattachment] = wrapper; } var bone : igbone = slot.bone; var flipx : int = skeleton.flipx ? -1 : 1; var flipy : int = skeleton.flipy ? -1 : 1; //this key part. need able set wrapper's affine2d's x , y position, the rotation angle (ie. rotation = someangle) //wrapper.affine.translate(bone.worldx, bone.worldy); //wrapper.affine.rotate(bone.worldrotationx * flipx * flipy); wrapper.scalex = bone.worldscalex * flipx; wrapper.scaley = bone.worldscaley * flipy; g.scale(wrapper.scalex/4, wrapper.scaley/4); g.translate(1000, 1600); // set position of widget g.rotate(bone.worldrotationx * flipx * flipy); g.multtransform(wrapper.affine); g.drawsubimage(m_image, wrapper.x, wrapper.y, wrapper.width, wrapper.height, 0, 0, wrapper.width, wrapper.height); } } g.pop(); } } public function skeleton () : igskeleton { return m_skeleton; } } and affine2d class:
package engine.math; public class igaffine2d { ///////////////////////////////////////////////////////////////////// // state ///////////////////////////////////////////////////////////////////// public var m0:double; public var m1:double; public var m2:double; public var m3:double; public var m4:double; public var m5:double; ///////////////////////////////////////////////////////////////////// // construction , initialsation ///////////////////////////////////////////////////////////////////// public final function igaffine2d() { this.init(); } public final function isidentity() : bool { return m0 == 1 && m2 == 0 && m4 == 0 && m1 == 0 && m3 == 1 && m5 == 0; } public final function init() : igaffine2d { m0 = 1.0; m1 = 0.0; m2 = 0.0; m3 = 1.0; m4 = 0.0; m5 = 0.0; return this; } public final function initcolumnmajor( a0 : double, a1 : double, a2 : double, a3 : double, a4 : double, a5 : double) : igaffine2d { // represents following 3x3 (2d affine) matrix // m0 m2 m4 // m1 m3 m5 // 0 0 1 m0 = a0; m1 = a1; m2 = a2; m3 = a3; m4 = a4; m5 = a5; return this; } public final function initfromtransform(transform:igaffine2d) : igaffine2d { m0 = transform.m0; m1 = transform.m1; m2 = transform.m2; m3 = transform.m3; m4 = transform.m4; m5 = transform.m5; return this; } public final function initwithinversefromtransform(other : igaffine2d) : igaffine2d { // 65 ops var : double= other.m0; var b : double= other.m2; var c : double= other.m1; var d : double= other.m3; var det_inv : double = 1.0 / (a*d - b*c); m0 = d * det_inv; m1 = -c * det_inv; m2 = -b * det_inv; m3 = * det_inv; var x : double= other.m4; var y : double= other.m5; m4 = -(x * m0 + y * m2); m5 = -(x * m1 + y * m3); return this; } ////////////////////////////////////////////////////////////////////// // getting properties of transform ////////////////////////////////////////////////////////////////////// public final function translate_x() : double { return m4; } public final function translate_y() : double { return m5; } public final function scale_x() : double { return m0; } public final function scale_y() : double { return m3; } /** * determines whether or not translation applied matrix * result in coordinate being integer bound point * around origin **/ public final function isintegertranslate() : bool { var tx : int = m4; var ty : int = m5; if (m0 == 1 && m1 == 0 && m2 == 0 && m3 == 1 && m4 == tx && m5 == ty) { return true; } return false; } ////////////////////////////////////////////////////////////////////// // performing transformations ////////////////////////////////////////////////////////////////////// public final function translate(dx:double, dy:double): void { m4 += (m0 * dx) + (m2 * dy); m5 += (m1 * dx) + (m3 * dy); } public final function rotate (theta: double) : void { if(theta == 0) { return; } var st :double = math.sin(theta); var ct :double = math.cos(theta); var r00 :double = (m0 * ct) + (m2 * st); var r01 :double = (m0 * -st) + (m2 * ct); var r02 :double = m4; var r10 :double = (m1 * ct) + (m3 * st); var r11 :double = (m1 * -st) + (m3 * ct); var r12 :double = m5; m0 = r00; m2 = r01; m4 = r02; m1 = r10; m3 = r11; m5 = r12; } public final function scale(sx : double, sy : double) : void { m0 *= sx; m1 *= sx; m2 *= sy; m3 *= sy; } // [m00 m01 m02] [1 shx 0] // [m10 m11 m12] [shy 1 0] // [ 0 0 1] [0 0 1] public final function shear(shx:double, shy:double) : void { var r00 : double = m0 + m2 * shy; var r01 : double = m0 * shx + m2; var r02 : double = m4; var r10 : double = m1 + m3 * shy; var r11 : double = m1 * shx + m3; var r12 : double = m5; m0 = r00; m2 = r01; m4 = r02; m1 = r10; m3 = r11; m5 = r12; } /** * performs translate/scale/rotate transformation around pivot point. * optimized version of: * - translate(dx + px, dy + py); * - rotate(theta); * - scale(sx, sy); * - translate(-px, -py); **/ public final function trs(dx : double, dy : double, px : double, py : double, // positive pivot point sx : double, sy : double, theta : double): igaffine2d { // abort standard case if (sx == 1.0 && sy == 1.0 && theta == 0) { m4 += dx; m5 += dy; return this; } var l0 : double = m0; var l1 : double = m1; var l2 : double = m2; var l3 : double = m3; var l4 : double = m4 + (dx + px)*l0 + (dy + py)*l2; var l5 : double = m5 + (dx + px)*l1 + (dy + py)*l3; if (theta != 0) { var s : double = -math.sin(theta); var c : double = math.cos(theta); var r0 : double = c*sx; var r1 : double = -s*sy; var r2 : double = s*sx; var r3 : double = c*sy; m0 = l0 * r0 + l2 * r1; m1 = l1 * r0 + l3 * r1; m2 = l0 * r2 + l2 * r3; m3 = l1 * r2 + l3 * r3; m4 = l4 - m0 * px - m2 * py; m5 = l5 - m1 * px - m3 * py; } else { m0 = l0 * sx ; m1 = l1 * sx ; m2 = l2 * sy; m3 = l3 * sy; m4 = l4 - (m0 * px) - (m2 * py); m5 = l5 - (m1 * px) - (m3 * py); } return this; } public final function initfromtransformwithoffset(other : igaffine2d, dx : double, dy : double) : igaffine2d { m0 = other.m0; m1 = other.m1; m2 = other.m2; m3 = other.m3; m4 = other.m4 + dx * m0 + dy * m2; m5 = other.m5 + dx * m1 + dy * m3; return this; } public final function initfromtransformwithtrs( other : igaffine2d, dx : double, dy : double, px : double, py : double, // positive pivot point sx : double, sy : double, theta : double): igaffine2d { var s :double = 0; var c :double = 1; if (theta != 0) { s = -math.sin(theta); c = math.cos(theta); } var l0 : double = other.m0; var l1 : double = other.m1; var l2 : double = other.m2; var l3 : double = other.m3; var l4 : double = other.m4 + (dx + px)*l0 + (dy + py)*l2; var l5 : double = other.m5 + (dx + px)*l1 + (dy + py)*l3; var r0 : double = c*sx; var r1 : double = -s*sy; var r2 : double = s*sx; var r3 : double = c*sy; m0 = l0 * r0 + l2 * r1; m1 = l1 * r0 + l3 * r1; m2 = l0 * r2 + l2 * r3; m3 = l1 * r2 + l3 * r3; m4 = l4 - m0 * px - m2 * py; m5 = l5 - m1 * px - m3 * py; return this; } ////////////////////////////////////////////////////////////////////// // performing other operations ////////////////////////////////////////////////////////////////////// public final function invert(): igaffine2d { var : double= m0; var b : double= m2; var c : double= m1; var d : double= m3; var det_inv : double = 1.0 / (a*d - b*c); m0 = d * det_inv; m1 = -c * det_inv; m2 = -b * det_inv; m3 = * det_inv; var x : double= m4; var y : double= m5; m4 = -x * m0 + -y * m2; m5 = -x * m1 + -y * m3; return this; } public final function concat(t : igaffine2d): igaffine2d { var r00 : double = (m0 * t.m0) + (m2 * t.m1); var r01 : double = (m0 * t.m2) + (m2 * t.m3); var r02 : double = (m0 * t.m4) + (m2 * t.m5) + m4; var r10 : double = (m1 * t.m0) + (m3 * t.m1); var r11 : double = (m1 * t.m2) + (m3 * t.m3); var r12 : double = (m1 * t.m4) + (m3 * t.m5) + m5; m0 = r00; m2 = r01; m4 = r02; m1 = r10; m3 = r11; m5 = r12; return this; } ////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////// public final function transformvector2(pin : igvector2, pout : igvector2): void { var x:double = pin.x * m0 + pin.y * m2 + m4; var y:double = pin.x * m1 + pin.y * m3 + m5; pout.x = x; pout.y = y; } public final function debug() : string { return m0 + " \t" + m2 + "\t" + m4 + "\n" + m1 + "\t" + m3 + "\t" + m5; } }
i've figured out. on each call need redraw based on position, reset affine coordinates, translate , rotate once correct position.
Comments
Post a Comment