ObjectFollower.hx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. package h2d;
  2. /**
  3. Follows the 3D object position in current 3D camera, synchronizing the follower position to projected 2D position of the followed object.
  4. **/
  5. @:uiNoComponent
  6. class ObjectFollower extends Object {
  7. /**
  8. Reference to target 3D object to follow.
  9. **/
  10. public var follow : h3d.scene.Object;
  11. /**
  12. Rounds the resulting 2d position of follower aligning it to s2d pixel grid.
  13. **/
  14. public var pixelSnap = true;
  15. /**
  16. If enabled, follower will mirror visibility of target object.
  17. **/
  18. public var followVisibility = false;
  19. /**
  20. Extra camera projection offset along X-axis to which follower will attach to.
  21. **/
  22. public var offsetX = 0.;
  23. /**
  24. Extra camera projection offset along Y-axis to which follower will attach to.
  25. **/
  26. public var offsetY = 0.;
  27. /**
  28. Extra camera projection offset along Z-axis to which follower will attach to.
  29. **/
  30. public var offsetZ = 0.;
  31. /**
  32. Horizontal object alignment relative to anchoring point.
  33. **/
  34. public var horizontalAlign : h2d.Flow.FlowAlign = Left;
  35. /**
  36. Vertical object alignment relative to anchoring point.
  37. **/
  38. public var verticalAlign : h2d.Flow.FlowAlign = Top;
  39. /**
  40. Mask with current depth buffer
  41. **/
  42. public var depthMask : Bool = false;
  43. /**
  44. Calculate the depth for masking with the given bias in 3D position units, relative to current camera.
  45. **/
  46. public var depthBias : Float = 0.;
  47. /**
  48. Express the offset in terms of the current camera direction.
  49. **/
  50. public var cameraRelative : Bool = false;
  51. var zValue : Float = 0.;
  52. var outputScale : Float = 1.;
  53. var tmpPos = new h3d.Vector();
  54. var tmpSize : h2d.col.Bounds;
  55. /**
  56. Create a new ObjectFollower instance.
  57. @param obj The 3D object to follow.
  58. @param parent An optional parent `h2d.Object` instance to which ObjectFollower adds itself if set.
  59. **/
  60. public function new( obj, ?parent ) {
  61. super(parent);
  62. this.follow = obj;
  63. }
  64. function followObject() {
  65. if( follow == null )
  66. return;
  67. var scene = @:privateAccess follow.getScene();
  68. if( scene == null )
  69. return;
  70. var s2d = getScene();
  71. var width = s2d == null ? h3d.Engine.getCurrent().width : s2d.width;
  72. var height = s2d == null ? h3d.Engine.getCurrent().height : s2d.height;
  73. var absPos = follow.getAbsPos();
  74. var pos = new h3d.Vector();
  75. if( cameraRelative ) {
  76. var m = new h3d.Matrix();
  77. inline m.load(scene.camera.mcam);
  78. inline m.transpose();
  79. var tmp = new h3d.Vector(offsetX, offsetZ, offsetY);
  80. tmp.transform3x3(m);
  81. pos.set(absPos._41 + tmp.x, absPos._42 + tmp.y, absPos._43 + tmp.z);
  82. } else {
  83. pos.set(absPos._41 + offsetX, absPos._42 + offsetY, absPos._43 + offsetZ);
  84. }
  85. var p = scene.camera.project(pos.x, pos.y, pos.z, width * outputScale, height * outputScale, tmpPos);
  86. zValue = p.z;
  87. if( horizontalAlign != Left || verticalAlign != Top ) {
  88. if( tmpSize == null ) tmpSize = new h2d.col.Bounds();
  89. var prev = follow;
  90. follow = null;
  91. var b = getSize(tmpSize); // prevent recursive
  92. follow = prev;
  93. var w = b.width, h = b.height;
  94. switch( horizontalAlign ) {
  95. case Middle: p.x -= w * 0.5;
  96. case Right: p.x -= w;
  97. default:
  98. }
  99. switch( verticalAlign ) {
  100. case Middle: p.y -= h * 0.5;
  101. case Bottom: p.y -= h;
  102. default:
  103. }
  104. }
  105. if( pixelSnap ) {
  106. p.x = Math.round(p.x);
  107. p.y = Math.round(p.y);
  108. }
  109. x = p.x;
  110. y = p.y;
  111. if( depthBias != 0 ) {
  112. var move = scene.camera.pos.sub(pos).normalized();
  113. pos.x += move.x * depthBias;
  114. pos.y += move.y * depthBias;
  115. pos.z += move.z * depthBias;
  116. var p2 = scene.camera.project(pos.x, pos.y, pos.z, width, height, tmpPos);
  117. zValue = p2.z;
  118. }
  119. }
  120. override function calcAbsPos() {
  121. super.calcAbsPos();
  122. absX = x;
  123. absY = y;
  124. }
  125. override function syncPos() {
  126. if( follow == null ) {
  127. if( posChanged ) {
  128. calcAbsPos();
  129. for( c in children )
  130. c.posChanged = true;
  131. posChanged = false;
  132. }
  133. return;
  134. }
  135. followObject();
  136. super.syncPos();
  137. }
  138. override function sync(ctx) {
  139. followObject();
  140. super.sync(ctx);
  141. }
  142. override function drawRec(ctx:RenderContext) {
  143. if( !visible || zValue < 0 || zValue > 1 )
  144. return;
  145. if( followVisibility ) {
  146. var parent = follow;
  147. while(parent != null) {
  148. if( !parent.visible )
  149. return;
  150. parent = parent.parent;
  151. }
  152. }
  153. if( !depthMask ) {
  154. super.drawRec(ctx);
  155. return;
  156. }
  157. @:privateAccess {
  158. var prev = ctx.baseShader.zValue;
  159. var prevMode = ctx.pass.depthTest, prevWrite = ctx.pass.depthWrite;
  160. if( prevMode != LessEqual ) {
  161. ctx.pass.depth(true, LessEqual);
  162. ctx.engine.selectMaterial(ctx.pass);
  163. }
  164. ctx.baseShader.zValue = zValue;
  165. super.drawRec(ctx);
  166. ctx.baseShader.zValue = prev;
  167. if( prevMode != LessEqual ) {
  168. ctx.pass.depth(prevWrite, prevMode);
  169. ctx.engine.selectMaterial(ctx.pass);
  170. }
  171. }
  172. }
  173. }