123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918 |
- var __extends = (this && this.__extends) || function (d, b) {
- for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
- function __() { this.constructor = d; }
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
- };
- var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
- return c > 3 && r && Object.defineProperty(target, key, r), r;
- };
- var BABYLON;
- (function (BABYLON) {
- var Canvas2DEngineBoundData = (function () {
- function Canvas2DEngineBoundData() {
- this._modelCache = new BABYLON.StringDictionary();
- }
- Canvas2DEngineBoundData.prototype.GetOrAddModelCache = function (key, factory) {
- return this._modelCache.getOrAddWithFactory(key, factory);
- };
- Canvas2DEngineBoundData.prototype.DisposeModelRenderCache = function (modelRenderCache) {
- if (!modelRenderCache.isDisposed) {
- return false;
- }
- this._modelCache.remove(modelRenderCache.modelKey);
- return true;
- };
- return Canvas2DEngineBoundData;
- })();
- BABYLON.Canvas2DEngineBoundData = Canvas2DEngineBoundData;
- var Canvas2D = (function (_super) {
- __extends(Canvas2D, _super);
- function Canvas2D() {
- var _this = this;
- _super.apply(this, arguments);
- /**
- * If you set your own WorldSpaceNode to display the Canvas2D you have to provide your own implementation of this method which computes the local position in the Canvas based on the given 3D World one.
- * Beware that you have to take under consideration the origin and the renderScaleFactor in your calculations! Good luck!
- */
- this.worldSpaceToNodeLocal = function (worldPos) {
- var node = _this._worldSpaceNode;
- if (!node) {
- return;
- }
- var mtx = node.getWorldMatrix().clone();
- mtx.invert();
- var v = BABYLON.Vector3.TransformCoordinates(worldPos, mtx);
- var rsf = _this._renderScaleFactor;
- var res = new BABYLON.Vector2(v.x * rsf, v.y * rsf);
- var size = _this.actualSize;
- var o = _this.origin;
- res.x += size.width * o.x;
- res.y += size.width * o.y;
- return res;
- };
- this._notifDebugMode = false;
- this._mapCounter = 0;
- }
- /**
- * Create a new 2D ScreenSpace Rendering Canvas, it is a 2D rectangle that has a size (width/height) and a position relative to the bottom/left corner of the screen.
- * ScreenSpace Canvas will be drawn in the Viewport as a 2D Layer lying to the top of the 3D Scene. Typically used for traditional UI.
- * All caching strategies will be available.
- * PLEASE NOTE: the origin of a Screen Space Canvas is set to [0;0] (bottom/left) which is different than the default origin of a Primitive which is centered [0.5;0.5]
- * @param scene the Scene that owns the Canvas
- * Options:
- * - id: a text identifier, for information purpose only
- * - pos: the position of the canvas, relative from the bottom/left of the scene's viewport. Alternatively you can set the x and y properties directly. Default value is [0, 0]
- * - size: the Size of the canvas. Alternatively the width and height properties can be set. If null two behaviors depend on the cachingStrategy: if it's CACHESTRATEGY_CACHECANVAS then it will always auto-fit the rendering device, in all the other modes it will fit the content of the Canvas
- * - cachingStrategy: either CACHESTRATEGY_TOPLEVELGROUPS, CACHESTRATEGY_ALLGROUPS, CACHESTRATEGY_CANVAS, CACHESTRATEGY_DONTCACHE. Please refer to their respective documentation for more information. Default is Canvas2D.CACHESTRATEGY_DONTCACHE
- * - enableInteraction: if true the pointer events will be listened and rerouted to the appropriate primitives of the Canvas2D through the Prim2DBase.onPointerEventObservable observable property. Default is true.
- * - isVisible: true if the canvas must be visible, false for hidden. Default is true.
- * - marginTop/Left/Right/Bottom: define the margin for the corresponding edge, if all of them are null, margin is not used in layout computing. Default Value is null for each.
- * - hAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
- * - vAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
- */
- Canvas2D.CreateScreenSpace = function (scene, options) {
- var c = new Canvas2D();
- if (!options) {
- c.setupCanvas(scene, null, null, 1, true, Canvas2D.CACHESTRATEGY_DONTCACHE, true, BABYLON.Vector2.Zero(), true, null, null, null, null, null, null);
- c.position = BABYLON.Vector2.Zero();
- }
- else {
- var pos = options.position || new BABYLON.Vector2(options.x || 0, options.y || 0);
- var size = (!options.size && !options.width && !options.height) ? null : (options.size || (new BABYLON.Size(options.width || 0, options.height || 0)));
- c.setupCanvas(scene, options.id || null, size, 1, true, options.cachingStrategy || Canvas2D.CACHESTRATEGY_DONTCACHE, options.enableInteraction || true, options.origin || BABYLON.Vector2.Zero(), options.isVisible || true, options.marginTop, options.marginLeft, options.marginRight, options.marginBottom, options.hAlignment || BABYLON.Prim2DBase.HAlignLeft, options.vAlignment || BABYLON.Prim2DBase.VAlignTop);
- c.position = pos;
- }
- return c;
- };
- /**
- * Create a new 2D WorldSpace Rendering Canvas, it is a 2D rectangle that has a size (width/height) and a world transformation information to place it in the world space.
- * This kind of canvas can't have its Primitives directly drawn in the Viewport, they need to be cached in a bitmap at some point, as a consequence the DONT_CACHE strategy is unavailable. For now only CACHESTRATEGY_CANVAS is supported, but the remaining strategies will be soon.
- * @param scene the Scene that owns the Canvas
- * @param size the dimension of the Canvas in World Space
- * Options:
- * - id: a text identifier, for information purpose only, default is null.
- * - position the position of the Canvas in World Space, default is [0,0,0]
- * - rotation the rotation of the Canvas in World Space, default is Quaternion.Identity()
- * - renderScaleFactor A scale factor applied to create the rendering texture that will be mapped in the Scene Rectangle. If you set 2 for instance the texture will be twice large in width and height. A greater value will allow to achieve a better rendering quality. Default value is 1.
- * BE AWARE that the Canvas true dimension will be size*renderScaleFactor, then all coordinates and size will have to be express regarding this size.
- * TIPS: if you want a renderScaleFactor independent reference of frame, create a child Group2D in the Canvas with position 0,0 and size set to null, then set its scale property to the same amount than the renderScaleFactor, put all your primitive inside using coordinates regarding the size property you pick for the Canvas and you'll be fine.
- * - sideOrientation: Unexpected behavior occur if the value is different from Mesh.DEFAULTSIDE right now, so please use this one, which is the default.
- * - cachingStrategy Must be CACHESTRATEGY_CANVAS for now, which is the default.
- * - enableInteraction: if true the pointer events will be listened and rerouted to the appropriate primitives of the Canvas2D through the Prim2DBase.onPointerEventObservable observable property. Default is false (the opposite of ScreenSpace).
- * - isVisible: true if the canvas must be visible, false for hidden. Default is true.
- * - customWorldSpaceNode: if specified the Canvas will be rendered in this given Node. But it's the responsibility of the caller to set the "worldSpaceToNodeLocal" property to compute the hit of the mouse ray into the node (in world coordinate system) as well as rendering the cached bitmap in the node itself. The properties cachedRect and cachedTexture of Group2D will give you what you need to do that.
- */
- Canvas2D.CreateWorldSpace = function (scene, size, options) {
- var cs = options && options.cachingStrategy || Canvas2D.CACHESTRATEGY_CANVAS;
- if (cs !== Canvas2D.CACHESTRATEGY_CANVAS) {
- throw new Error("Right now only the CACHESTRATEGY_CANVAS cache Strategy is supported for WorldSpace Canvas. More will come soon!");
- }
- //if (cachingStrategy === Canvas2D.CACHESTRATEGY_DONTCACHE) {
- // throw new Error("CACHESTRATEGY_DONTCACHE cache Strategy can't be used for WorldSpace Canvas");
- //}
- var enableInteraction = options ? options.enableInteraction : true;
- var createWorldSpaceNode = !options || (options.customWorldSpaceNode == null);
- var isVisible = options ? options.isVisible || true : true;
- var id = options ? options.id || null : null;
- var rsf = options ? options.renderScaleFactor || 1 : 1;
- var c = new Canvas2D();
- c.setupCanvas(scene, id, new BABYLON.Size(size.width, size.height), rsf, false, cs, enableInteraction, new BABYLON.Vector2(0.5, 0.5), isVisible, null, null, null, null, null, null);
- if (createWorldSpaceNode) {
- var plane = new BABYLON.WorldSpaceCanvas2D(id, scene, c);
- var vertexData = BABYLON.VertexData.CreatePlane({
- width: size.width,
- height: size.height,
- sideOrientation: options && options.sideOrientation || BABYLON.Mesh.DEFAULTSIDE
- });
- var mtl = new BABYLON.StandardMaterial(id + "_Material", scene);
- c.applyCachedTexture(vertexData, mtl);
- vertexData.applyToMesh(plane, false);
- mtl.specularColor = new BABYLON.Color3(0, 0, 0);
- mtl.disableLighting = true;
- mtl.useAlphaFromDiffuseTexture = true;
- plane.position = options && options.position || BABYLON.Vector3.Zero();
- plane.rotationQuaternion = options && options.rotation || BABYLON.Quaternion.Identity();
- plane.material = mtl;
- c._worldSpaceNode = plane;
- }
- else {
- c._worldSpaceNode = options.customWorldSpaceNode;
- }
- return c;
- };
- Canvas2D.prototype.setupCanvas = function (scene, name, size, renderScaleFactor, isScreenSpace, cachingstrategy, enableInteraction, origin, isVisible, marginTop, marginLeft, marginRight, marginBottom, hAlign, vAlign) {
- var _this = this;
- var engine = scene.getEngine();
- this._fitRenderingDevice = !size;
- if (!size) {
- size = new BABYLON.Size(engine.getRenderWidth(), engine.getRenderHeight());
- }
- else {
- size.height *= renderScaleFactor;
- size.width *= renderScaleFactor;
- }
- this.__engineData = engine.getOrAddExternalDataWithFactory("__BJSCANVAS2D__", function (k) { return new Canvas2DEngineBoundData(); });
- this._renderScaleFactor = renderScaleFactor;
- this._cachingStrategy = cachingstrategy;
- this._primPointerInfo = new BABYLON.PrimitivePointerInfo();
- this._capturedPointers = new BABYLON.StringDictionary();
- this._pickStartingPosition = BABYLON.Vector2.Zero();
- this.setupGroup2D(this, null, name, BABYLON.Vector2.Zero(), origin, size, isVisible, this._cachingStrategy === Canvas2D.CACHESTRATEGY_ALLGROUPS ? BABYLON.Group2D.GROUPCACHEBEHAVIOR_DONTCACHEOVERRIDE : BABYLON.Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY, marginTop, marginLeft, marginRight, marginBottom, hAlign, vAlign);
- this._hierarchyLevelMaxSiblingCount = 10;
- this._hierarchyDepthOffset = 0;
- this._siblingDepthOffset = 1 / this._hierarchyLevelMaxSiblingCount;
- this._scene = scene;
- this._engine = engine;
- this._renderingSize = new BABYLON.Size(0, 0);
- // Register scene dispose to also dispose the canvas when it'll happens
- scene.onDisposeObservable.add(function (d, s) {
- _this.dispose();
- });
- if (cachingstrategy !== Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS) {
- this._background = BABYLON.Rectangle2D.Create(this, { id: "###CANVAS BACKGROUND###", width: size.width, height: size.height });
- this._background.isPickable = false;
- this._background.origin = BABYLON.Vector2.Zero();
- this._background.levelVisible = false;
- }
- this._isScreeSpace = isScreenSpace;
- if (this._isScreeSpace) {
- this._afterRenderObserver = this._scene.onAfterRenderObservable.add(function (d, s) {
- _this._engine.clear(null, false, true);
- _this._render();
- });
- }
- else {
- this._beforeRenderObserver = this._scene.onBeforeRenderObservable.add(function (d, s) {
- _this._render();
- });
- }
- this._supprtInstancedArray = this._engine.getCaps().instancedArrays !== null;
- // this._supprtInstancedArray = false; // TODO REMOVE!!!
- this._setupInteraction(enableInteraction);
- };
- Object.defineProperty(Canvas2D.prototype, "hierarchyLevelMaxSiblingCount", {
- get: function () {
- return this._hierarchyLevelMaxSiblingCount;
- },
- enumerable: true,
- configurable: true
- });
- Canvas2D.prototype._setupInteraction = function (enable) {
- var _this = this;
- // No change detection
- if (enable === this._interactionEnabled) {
- return;
- }
- // Set the new state
- this._interactionEnabled = enable;
- // ScreenSpace mode
- if (this._isScreeSpace) {
- // Disable interaction
- if (!enable) {
- if (this._scenePrePointerObserver) {
- this.scene.onPrePointerObservable.remove(this._scenePrePointerObserver);
- this._scenePrePointerObserver = null;
- }
- return;
- }
- // Enable Interaction
- // Register the observable
- this._scenePrePointerObserver = this.scene.onPrePointerObservable.add(function (e, s) {
- var hs = 1 / _this.engine.getHardwareScalingLevel();
- var localPos = e.localPosition.multiplyByFloats(hs, hs);
- _this._handlePointerEventForInteraction(e, localPos, s);
- });
- }
- else {
- var scene = this.scene;
- if (enable) {
- scene.constantlyUpdateMeshUnderPointer = true;
- this._scenePointerObserver = scene.onPointerObservable.add(function (e, s) {
- if (e.pickInfo.hit && e.pickInfo.pickedMesh === _this._worldSpaceNode && _this.worldSpaceToNodeLocal) {
- var localPos = _this.worldSpaceToNodeLocal(e.pickInfo.pickedPoint);
- _this._handlePointerEventForInteraction(e, localPos, s);
- }
- });
- }
- else {
- if (this._scenePointerObserver) {
- this.scene.onPointerObservable.remove(this._scenePointerObserver);
- this._scenePointerObserver = null;
- }
- }
- }
- };
- /**
- * Internal method, you should use the Prim2DBase version instead
- */
- Canvas2D.prototype._setPointerCapture = function (pointerId, primitive) {
- if (this.isPointerCaptured(pointerId)) {
- return false;
- }
- // Try to capture the pointer on the HTML side
- try {
- this.engine.getRenderingCanvas().setPointerCapture(pointerId);
- }
- catch (e) {
- }
- this._primPointerInfo.updateRelatedTarget(primitive, BABYLON.Vector2.Zero());
- this._bubbleNotifyPrimPointerObserver(primitive, BABYLON.PrimitivePointerInfo.PointerGotCapture, null);
- this._capturedPointers.add(pointerId.toString(), primitive);
- return true;
- };
- /**
- * Internal method, you should use the Prim2DBase version instead
- */
- Canvas2D.prototype._releasePointerCapture = function (pointerId, primitive) {
- if (this._capturedPointers.get(pointerId.toString()) !== primitive) {
- return false;
- }
- // Try to release the pointer on the HTML side
- try {
- this.engine.getRenderingCanvas().releasePointerCapture(pointerId);
- }
- catch (e) {
- }
- this._primPointerInfo.updateRelatedTarget(primitive, BABYLON.Vector2.Zero());
- this._bubbleNotifyPrimPointerObserver(primitive, BABYLON.PrimitivePointerInfo.PointerLostCapture, null);
- this._capturedPointers.remove(pointerId.toString());
- return true;
- };
- /**
- * Determine if the given pointer is captured or not
- * @param pointerId the Id of the pointer
- * @return true if it's captured, false otherwise
- */
- Canvas2D.prototype.isPointerCaptured = function (pointerId) {
- return this._capturedPointers.contains(pointerId.toString());
- };
- Canvas2D.prototype.getCapturedPrimitive = function (pointerId) {
- // Avoid unnecessary lookup
- if (this._capturedPointers.count === 0) {
- return null;
- }
- return this._capturedPointers.get(pointerId.toString());
- };
- Canvas2D.prototype._handlePointerEventForInteraction = function (eventData, localPosition, eventState) {
- // Dispose check
- if (this.isDisposed) {
- return;
- }
- // Update the this._primPointerInfo structure we'll send to observers using the PointerEvent data
- this._updatePointerInfo(eventData, localPosition);
- var capturedPrim = this.getCapturedPrimitive(this._primPointerInfo.pointerId);
- // Make sure the intersection list is up to date, we maintain this list either in response of a mouse event (here) or before rendering the canvas.
- // Why before rendering the canvas? because some primitives may move and get away/under the mouse cursor (which is not moving). So we need to update at both location in order to always have an accurate list, which is needed for the hover state change.
- this._updateIntersectionList(this._primPointerInfo.canvasPointerPos, capturedPrim !== null);
- // Update the over status, same as above, it's could be done here or during rendering, but will be performed only once per render frame
- this._updateOverStatus();
- // Check if we have nothing to raise
- if (!this._actualOverPrimitive && !capturedPrim) {
- return;
- }
- // Update the relatedTarget info with the over primitive or the captured one (if any)
- var targetPrim = capturedPrim || this._actualOverPrimitive.prim;
- var targetPointerPos = capturedPrim ? this._primPointerInfo.canvasPointerPos.subtract(new BABYLON.Vector2(targetPrim.globalTransform.m[12], targetPrim.globalTransform.m[13])) : this._actualOverPrimitive.intersectionLocation;
- this._primPointerInfo.updateRelatedTarget(targetPrim, targetPointerPos);
- // Analyze the pointer event type and fire proper events on the primitive
- if (eventData.type === BABYLON.PointerEventTypes.POINTERWHEEL) {
- this._bubbleNotifyPrimPointerObserver(targetPrim, BABYLON.PrimitivePointerInfo.PointerMouseWheel, eventData.event);
- }
- else if (eventData.type === BABYLON.PointerEventTypes.POINTERMOVE) {
- this._bubbleNotifyPrimPointerObserver(targetPrim, BABYLON.PrimitivePointerInfo.PointerMove, eventData.event);
- }
- else if (eventData.type === BABYLON.PointerEventTypes.POINTERDOWN) {
- this._bubbleNotifyPrimPointerObserver(targetPrim, BABYLON.PrimitivePointerInfo.PointerDown, eventData.event);
- }
- else if (eventData.type === BABYLON.PointerEventTypes.POINTERUP) {
- this._bubbleNotifyPrimPointerObserver(targetPrim, BABYLON.PrimitivePointerInfo.PointerUp, eventData.event);
- }
- };
- Canvas2D.prototype._updatePointerInfo = function (eventData, localPosition) {
- var pii = this._primPointerInfo;
- if (!pii.canvasPointerPos) {
- pii.canvasPointerPos = BABYLON.Vector2.Zero();
- }
- var camera = this._scene.activeCamera;
- var engine = this._scene.getEngine();
- if (this._isScreeSpace) {
- var cameraViewport = camera.viewport;
- var viewport = cameraViewport.toGlobal(engine.getRenderWidth(), engine.getRenderHeight());
- // Moving coordinates to local viewport world
- var x = localPosition.x - viewport.x;
- var y = localPosition.y - viewport.y;
- pii.canvasPointerPos.x = x - this.position.x;
- pii.canvasPointerPos.y = engine.getRenderHeight() - y - this.position.y;
- }
- else {
- pii.canvasPointerPos.x = localPosition.x;
- pii.canvasPointerPos.y = localPosition.y;
- }
- pii.mouseWheelDelta = 0;
- if (eventData.type === BABYLON.PointerEventTypes.POINTERWHEEL) {
- var event = eventData.event;
- if (event.wheelDelta) {
- pii.mouseWheelDelta = event.wheelDelta / (BABYLON.PrimitivePointerInfo.MouseWheelPrecision * 40);
- }
- else if (event.detail) {
- pii.mouseWheelDelta = -event.detail / BABYLON.PrimitivePointerInfo.MouseWheelPrecision;
- }
- }
- else {
- var pe = eventData.event;
- pii.ctrlKey = pe.ctrlKey;
- pii.altKey = pe.altKey;
- pii.shiftKey = pe.shiftKey;
- pii.metaKey = pe.metaKey;
- pii.button = pe.button;
- pii.buttons = pe.buttons;
- pii.pointerId = pe.pointerId;
- pii.width = pe.width;
- pii.height = pe.height;
- pii.presssure = pe.pressure;
- pii.tilt.x = pe.tiltX;
- pii.tilt.y = pe.tiltY;
- pii.isCaptured = this.getCapturedPrimitive(pe.pointerId) !== null;
- }
- };
- Canvas2D.prototype._updateIntersectionList = function (mouseLocalPos, isCapture) {
- if (this.scene.getRenderId() === this._intersectionRenderId) {
- return;
- }
- var ii = Canvas2D._interInfo;
- ii.pickPosition.x = mouseLocalPos.x;
- ii.pickPosition.y = mouseLocalPos.y;
- ii.findFirstOnly = false;
- // Fast rejection: test if the mouse pointer is outside the canvas's bounding Info
- if (!isCapture && !this.boundingInfo.doesIntersect(ii.pickPosition)) {
- this._previousIntersectionList = this._actualIntersectionList;
- this._actualIntersectionList = null;
- this._previousOverPrimitive = this._actualOverPrimitive;
- this._actualOverPrimitive = null;
- return;
- }
- this.intersect(ii);
- this._previousIntersectionList = this._actualIntersectionList;
- this._actualIntersectionList = ii.intersectedPrimitives;
- this._previousOverPrimitive = this._actualOverPrimitive;
- this._actualOverPrimitive = ii.topMostIntersectedPrimitive;
- this._intersectionRenderId = this.scene.getRenderId();
- };
- // Based on the previousIntersectionList and the actualInstersectionList we can determined which primitives are being hover state or loosing it
- Canvas2D.prototype._updateOverStatus = function () {
- if ((this.scene.getRenderId() === this._hoverStatusRenderId) || !this._previousIntersectionList || !this._actualIntersectionList) {
- return;
- }
- // Detect a change of over
- var prevPrim = this._previousOverPrimitive ? this._previousOverPrimitive.prim : null;
- var actualPrim = this._actualOverPrimitive ? this._actualOverPrimitive.prim : null;
- if (prevPrim !== actualPrim) {
- // Detect if the current pointer is captured, only fire event if they belong to the capture primitive
- var capturedPrim = this.getCapturedPrimitive(this._primPointerInfo.pointerId);
- // Notify the previous "over" prim that the pointer is no longer over it
- if ((capturedPrim && capturedPrim === prevPrim) || (!capturedPrim && prevPrim)) {
- this._primPointerInfo.updateRelatedTarget(prevPrim, this._previousOverPrimitive.intersectionLocation);
- this._bubbleNotifyPrimPointerObserver(prevPrim, BABYLON.PrimitivePointerInfo.PointerOut, null);
- }
- // Notify the new "over" prim that the pointer is over it
- if ((capturedPrim && capturedPrim === actualPrim) || (!capturedPrim && actualPrim)) {
- this._primPointerInfo.updateRelatedTarget(actualPrim, this._actualOverPrimitive.intersectionLocation);
- this._bubbleNotifyPrimPointerObserver(actualPrim, BABYLON.PrimitivePointerInfo.PointerOver, null);
- }
- }
- this._hoverStatusRenderId = this.scene.getRenderId();
- };
- Canvas2D.prototype._updatePrimPointerPos = function (prim) {
- if (this._primPointerInfo.isCaptured) {
- this._primPointerInfo.primitivePointerPos = this._primPointerInfo.relatedTargetPointerPos;
- }
- else {
- for (var _i = 0, _a = this._actualIntersectionList; _i < _a.length; _i++) {
- var pii = _a[_i];
- if (pii.prim === prim) {
- this._primPointerInfo.primitivePointerPos = pii.intersectionLocation;
- return;
- }
- }
- }
- };
- Canvas2D.prototype._debugExecObserver = function (prim, mask) {
- if (!this._notifDebugMode) {
- return;
- }
- var debug = "";
- for (var i = 0; i < prim.hierarchyDepth; i++) {
- debug += " ";
- }
- var pii = this._primPointerInfo;
- debug += "[RID:" + this.scene.getRenderId() + "] [" + prim.hierarchyDepth + "] event:" + BABYLON.PrimitivePointerInfo.getEventTypeName(mask) + ", id: " + prim.id + " (" + BABYLON.Tools.getClassName(prim) + "), primPos: " + pii.primitivePointerPos.toString() + ", canvasPos: " + pii.canvasPointerPos.toString();
- console.log(debug);
- };
- Canvas2D.prototype._bubbleNotifyPrimPointerObserver = function (prim, mask, eventData) {
- var ppi = this._primPointerInfo;
- // In case of PointerOver/Out we will first notify the children (but the deepest to the closest) with PointerEnter/Leave
- if ((mask & (BABYLON.PrimitivePointerInfo.PointerOver | BABYLON.PrimitivePointerInfo.PointerOut)) !== 0) {
- this._notifChildren(prim, mask);
- }
- var bubbleCancelled = false;
- var cur = prim;
- while (cur) {
- // Only trigger the observers if the primitive is intersected (except for out)
- if (!bubbleCancelled) {
- this._updatePrimPointerPos(cur);
- // Exec the observers
- this._debugExecObserver(cur, mask);
- cur._pointerEventObservable.notifyObservers(ppi, mask);
- this._triggerActionManager(cur, ppi, mask, eventData);
- // Bubble canceled? If we're not executing PointerOver or PointerOut, quit immediately
- // If it's PointerOver/Out we have to trigger PointerEnter/Leave no matter what
- if (ppi.cancelBubble) {
- if ((mask & (BABYLON.PrimitivePointerInfo.PointerOver | BABYLON.PrimitivePointerInfo.PointerOut)) === 0) {
- return;
- }
- // We're dealing with PointerOver/Out, let's keep looping to fire PointerEnter/Leave, but not Over/Out anymore
- bubbleCancelled = true;
- }
- }
- // If bubble is cancel we didn't update the Primitive Pointer Pos yet, let's do it
- if (bubbleCancelled) {
- this._updatePrimPointerPos(cur);
- }
- // Trigger a PointerEnter corresponding to the PointerOver
- if (mask === BABYLON.PrimitivePointerInfo.PointerOver) {
- this._debugExecObserver(cur, BABYLON.PrimitivePointerInfo.PointerEnter);
- cur._pointerEventObservable.notifyObservers(ppi, BABYLON.PrimitivePointerInfo.PointerEnter);
- }
- else if (mask === BABYLON.PrimitivePointerInfo.PointerOut) {
- this._debugExecObserver(cur, BABYLON.PrimitivePointerInfo.PointerLeave);
- cur._pointerEventObservable.notifyObservers(ppi, BABYLON.PrimitivePointerInfo.PointerLeave);
- }
- // Loop to the parent
- cur = cur.parent;
- }
- };
- Canvas2D.prototype._triggerActionManager = function (prim, ppi, mask, eventData) {
- var _this = this;
- // Process Trigger related to PointerDown
- if ((mask & BABYLON.PrimitivePointerInfo.PointerDown) !== 0) {
- // On pointer down, record the current position and time to be able to trick PickTrigger and LongPressTrigger
- this._pickStartingPosition = ppi.primitivePointerPos.clone();
- this._pickStartingTime = new Date().getTime();
- this._pickedDownPrim = null;
- if (prim.actionManager) {
- this._pickedDownPrim = prim;
- if (prim.actionManager.hasPickTriggers) {
- var actionEvent = BABYLON.ActionEvent.CreateNewFromPrimitive(prim, ppi.primitivePointerPos, eventData);
- switch (eventData.button) {
- case 0:
- prim.actionManager.processTrigger(BABYLON.ActionManager.OnLeftPickTrigger, actionEvent);
- break;
- case 1:
- prim.actionManager.processTrigger(BABYLON.ActionManager.OnCenterPickTrigger, actionEvent);
- break;
- case 2:
- prim.actionManager.processTrigger(BABYLON.ActionManager.OnRightPickTrigger, actionEvent);
- break;
- }
- prim.actionManager.processTrigger(BABYLON.ActionManager.OnPickDownTrigger, actionEvent);
- }
- if (prim.actionManager.hasSpecificTrigger(BABYLON.ActionManager.OnLongPressTrigger)) {
- window.setTimeout(function () {
- var ppi = _this._primPointerInfo;
- var capturedPrim = _this.getCapturedPrimitive(ppi.pointerId);
- _this._updateIntersectionList(ppi.canvasPointerPos, capturedPrim !== null);
- var ii = new BABYLON.IntersectInfo2D();
- ii.pickPosition = ppi.canvasPointerPos.clone();
- ii.findFirstOnly = false;
- _this.intersect(ii);
- if (ii.isPrimIntersected(prim) !== null) {
- if (prim.actionManager) {
- if (_this._pickStartingTime !== 0 && ((new Date().getTime() - _this._pickStartingTime) > BABYLON.ActionManager.LongPressDelay) && (Math.abs(_this._pickStartingPosition.x - ii.pickPosition.x) < BABYLON.ActionManager.DragMovementThreshold && Math.abs(_this._pickStartingPosition.y - ii.pickPosition.y) < BABYLON.ActionManager.DragMovementThreshold)) {
- _this._pickStartingTime = 0;
- prim.actionManager.processTrigger(BABYLON.ActionManager.OnLongPressTrigger, BABYLON.ActionEvent.CreateNewFromPrimitive(prim, ppi.primitivePointerPos, eventData));
- }
- }
- }
- }, BABYLON.ActionManager.LongPressDelay);
- }
- }
- }
- else if ((mask & BABYLON.PrimitivePointerInfo.PointerUp) !== 0) {
- this._pickStartingTime = 0;
- var actionEvent = BABYLON.ActionEvent.CreateNewFromPrimitive(prim, ppi.primitivePointerPos, eventData);
- if (prim.actionManager) {
- // OnPickUpTrigger
- prim.actionManager.processTrigger(BABYLON.ActionManager.OnPickUpTrigger, actionEvent);
- // OnPickTrigger
- if (Math.abs(this._pickStartingPosition.x - ppi.canvasPointerPos.x) < BABYLON.ActionManager.DragMovementThreshold && Math.abs(this._pickStartingPosition.y - ppi.canvasPointerPos.y) < BABYLON.ActionManager.DragMovementThreshold) {
- prim.actionManager.processTrigger(BABYLON.ActionManager.OnPickTrigger, actionEvent);
- }
- }
- // OnPickOutTrigger
- if (this._pickedDownPrim && this._pickedDownPrim.actionManager && (this._pickedDownPrim !== prim)) {
- this._pickedDownPrim.actionManager.processTrigger(BABYLON.ActionManager.OnPickOutTrigger, actionEvent);
- }
- }
- else if ((mask & BABYLON.PrimitivePointerInfo.PointerOver) !== 0) {
- if (prim.actionManager) {
- var actionEvent = BABYLON.ActionEvent.CreateNewFromPrimitive(prim, ppi.primitivePointerPos, eventData);
- prim.actionManager.processTrigger(BABYLON.ActionManager.OnPointerOverTrigger, actionEvent);
- }
- }
- else if ((mask & BABYLON.PrimitivePointerInfo.PointerOut) !== 0) {
- if (prim.actionManager) {
- var actionEvent = BABYLON.ActionEvent.CreateNewFromPrimitive(prim, ppi.primitivePointerPos, eventData);
- prim.actionManager.processTrigger(BABYLON.ActionManager.OnPointerOutTrigger, actionEvent);
- }
- }
- };
- Canvas2D.prototype._notifChildren = function (prim, mask) {
- var _this = this;
- var pii = this._primPointerInfo;
- prim.children.forEach(function (curChild) {
- // Recurse first, we want the deepest to be notified first
- _this._notifChildren(curChild, mask);
- _this._updatePrimPointerPos(curChild);
- // Fire the proper notification
- if (mask === BABYLON.PrimitivePointerInfo.PointerOver) {
- _this._debugExecObserver(curChild, BABYLON.PrimitivePointerInfo.PointerEnter);
- curChild._pointerEventObservable.notifyObservers(pii, BABYLON.PrimitivePointerInfo.PointerEnter);
- }
- else if (mask === BABYLON.PrimitivePointerInfo.PointerOut) {
- _this._debugExecObserver(curChild, BABYLON.PrimitivePointerInfo.PointerLeave);
- curChild._pointerEventObservable.notifyObservers(pii, BABYLON.PrimitivePointerInfo.PointerLeave);
- }
- });
- };
- /**
- * Don't forget to call the dispose method when you're done with the Canvas instance.
- * But don't worry, if you dispose its scene, the canvas will be automatically disposed too.
- */
- Canvas2D.prototype.dispose = function () {
- if (!_super.prototype.dispose.call(this)) {
- return false;
- }
- if (this.interactionEnabled) {
- this._setupInteraction(false);
- }
- if (this._beforeRenderObserver) {
- this._scene.onBeforeRenderObservable.remove(this._beforeRenderObserver);
- this._beforeRenderObserver = null;
- }
- if (this._afterRenderObserver) {
- this._scene.onAfterRenderObservable.remove(this._afterRenderObserver);
- this._afterRenderObserver = null;
- }
- if (this._groupCacheMaps) {
- this._groupCacheMaps.forEach(function (m) { return m.dispose(); });
- this._groupCacheMaps = null;
- }
- };
- Object.defineProperty(Canvas2D.prototype, "scene", {
- /**
- * Accessor to the Scene that owns the Canvas
- * @returns The instance of the Scene object
- */
- get: function () {
- return this._scene;
- },
- enumerable: true,
- configurable: true
- });
- Object.defineProperty(Canvas2D.prototype, "engine", {
- /**
- * Accessor to the Engine that drives the Scene used by this Canvas
- * @returns The instance of the Engine object
- */
- get: function () {
- return this._engine;
- },
- enumerable: true,
- configurable: true
- });
- Object.defineProperty(Canvas2D.prototype, "cachingStrategy", {
- /**
- * Accessor of the Caching Strategy used by this Canvas.
- * See Canvas2D.CACHESTRATEGY_xxxx static members for more information
- * @returns the value corresponding to the used strategy.
- */
- get: function () {
- return this._cachingStrategy;
- },
- enumerable: true,
- configurable: true
- });
- Object.defineProperty(Canvas2D.prototype, "worldSpaceCanvasNode", {
- /**
- * Only valid for World Space Canvas, returns the scene node that displays the canvas
- */
- get: function () {
- return this._worldSpaceNode;
- },
- enumerable: true,
- configurable: true
- });
- Object.defineProperty(Canvas2D.prototype, "supportInstancedArray", {
- /**
- * Check if the WebGL Instanced Array extension is supported or not
- * @returns {}
- */
- get: function () {
- return this._supprtInstancedArray;
- },
- enumerable: true,
- configurable: true
- });
- Object.defineProperty(Canvas2D.prototype, "backgroundFill", {
- /**
- * Property that defines the fill object used to draw the background of the Canvas.
- * Note that Canvas with a Caching Strategy of
- * @returns If the background is not set, null will be returned, otherwise a valid fill object is returned.
- */
- get: function () {
- if (!this._background || !this._background.isVisible) {
- return null;
- }
- return this._background.fill;
- },
- set: function (value) {
- this.checkBackgroundAvailability();
- if (value === this._background.fill) {
- return;
- }
- this._background.fill = value;
- this._background.levelVisible = true;
- },
- enumerable: true,
- configurable: true
- });
- Object.defineProperty(Canvas2D.prototype, "backgroundBorder", {
- /**
- * Property that defines the border object used to draw the background of the Canvas.
- * @returns If the background is not set, null will be returned, otherwise a valid border object is returned.
- */
- get: function () {
- if (!this._background || !this._background.isVisible) {
- return null;
- }
- return this._background.border;
- },
- set: function (value) {
- this.checkBackgroundAvailability();
- if (value === this._background.border) {
- return;
- }
- this._background.border = value;
- this._background.levelVisible = true;
- },
- enumerable: true,
- configurable: true
- });
- Object.defineProperty(Canvas2D.prototype, "backgroundRoundRadius", {
- /**
- * You can set the roundRadius of the background
- * @returns The current roundRadius
- */
- get: function () {
- if (!this._background || !this._background.isVisible) {
- return null;
- }
- return this._background.roundRadius;
- },
- set: function (value) {
- this.checkBackgroundAvailability();
- if (value === this._background.roundRadius) {
- return;
- }
- this._background.roundRadius = value;
- this._background.levelVisible = true;
- },
- enumerable: true,
- configurable: true
- });
- Object.defineProperty(Canvas2D.prototype, "interactionEnabled", {
- /**
- * Enable/Disable interaction for this Canvas
- * When enabled the Prim2DBase.pointerEventObservable property will notified when appropriate events occur
- */
- get: function () {
- return this._interactionEnabled;
- },
- set: function (enable) {
- this._setupInteraction(enable);
- },
- enumerable: true,
- configurable: true
- });
- Object.defineProperty(Canvas2D.prototype, "_engineData", {
- get: function () {
- return this.__engineData;
- },
- enumerable: true,
- configurable: true
- });
- Canvas2D.prototype.checkBackgroundAvailability = function () {
- if (this._cachingStrategy === Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS) {
- throw Error("Can't use Canvas Background with the caching strategy TOPLEVELGROUPS");
- }
- };
- Canvas2D.prototype.onPrimBecomesDirty = function () {
- this._addPrimToDirtyList(this);
- };
- Canvas2D.prototype._updateCanvasState = function () {
- // Check if the update has already been made for this render Frame
- if (this.scene.getRenderId() === this._updateRenderId) {
- return;
- }
- this._renderingSize.width = this.engine.getRenderWidth();
- this._renderingSize.height = this.engine.getRenderHeight();
- if (this._fitRenderingDevice) {
- this.size = this._renderingSize;
- if (this._background) {
- this._background.size = this.size;
- }
- }
- var context = new BABYLON.PrepareRender2DContext();
- ++this._globalTransformProcessStep;
- this.updateGlobalTransVis(false);
- this._prepareGroupRender(context);
- this._updateRenderId = this.scene.getRenderId();
- };
- /**
- * Method that renders the Canvas, you should not invoke
- */
- Canvas2D.prototype._render = function () {
- this._updateCanvasState();
- if (this._primPointerInfo.canvasPointerPos) {
- this._updateIntersectionList(this._primPointerInfo.canvasPointerPos, false);
- this._updateOverStatus(); // TODO this._primPointerInfo may not be up to date!
- }
- this._groupRender();
- // If the canvas is cached at canvas level, we must manually render the sprite that will display its content
- if (this._cachingStrategy === Canvas2D.CACHESTRATEGY_CANVAS && this._cachedCanvasGroup) {
- this._cachedCanvasGroup._renderCachedCanvas();
- }
- };
- /**
- * Internal method that allocate a cache for the given group.
- * Caching is made using a collection of MapTexture where many groups have their bitmap cache stored inside.
- * @param group The group to allocate the cache of.
- * @return custom type with the PackedRect instance giving information about the cache location into the texture and also the MapTexture instance that stores the cache.
- */
- Canvas2D.prototype._allocateGroupCache = function (group, parent, minSize) {
- // Determine size
- var size = group.actualSize;
- size = new BABYLON.Size(Math.ceil(size.width), Math.ceil(size.height));
- if (minSize) {
- size.width = Math.max(minSize.width, size.width);
- size.height = Math.max(minSize.height, size.height);
- }
- if (!this._groupCacheMaps) {
- this._groupCacheMaps = new Array();
- }
- // Try to find a spot in one of the cached texture
- var res = null;
- for (var _i = 0, _a = this._groupCacheMaps; _i < _a.length; _i++) {
- var map = _a[_i];
- var node = map.allocateRect(size);
- if (node) {
- res = { node: node, texture: map };
- break;
- }
- }
- // Couldn't find a map that could fit the rect, create a new map for it
- if (!res) {
- var mapSize = new BABYLON.Size(Canvas2D._groupTextureCacheSize, Canvas2D._groupTextureCacheSize);
- // Check if the predefined size would fit, other create a custom size using the nearest bigger power of 2
- if (size.width > mapSize.width || size.height > mapSize.height) {
- mapSize.width = Math.pow(2, Math.ceil(Math.log(size.width) / Math.log(2)));
- mapSize.height = Math.pow(2, Math.ceil(Math.log(size.height) / Math.log(2)));
- }
- var id = "groupsMapChache" + this._mapCounter + "forCanvas" + this.id;
- map = new BABYLON.MapTexture(id, this._scene, mapSize);
- this._groupCacheMaps.push(map);
- var node = map.allocateRect(size);
- res = { node: node, texture: map };
- }
- // Check if we have to create a Sprite that will display the content of the Canvas which is cached.
- // Don't do it in case of the group being a worldspace canvas (because its texture is bound to a WorldSpaceCanvas node)
- if (group !== this || this._isScreeSpace) {
- var node = res.node;
- // Special case if the canvas is entirely cached: create a group that will have a single sprite it will be rendered specifically at the very end of the rendering process
- if (this._cachingStrategy === Canvas2D.CACHESTRATEGY_CANVAS) {
- this._cachedCanvasGroup = BABYLON.Group2D._createCachedCanvasGroup(this);
- var sprite = BABYLON.Sprite2D.Create(this._cachedCanvasGroup, map, { id: "__cachedCanvasSprite__", spriteSize: node.contentSize, spriteLocation: node.pos });
- sprite.zOrder = 1;
- sprite.origin = BABYLON.Vector2.Zero();
- }
- else {
- var sprite = BABYLON.Sprite2D.Create(parent, map, { id: "__cachedSpriteOfGroup__" + group.id, x: group.position.x, y: group.position.y, spriteSize: node.contentSize, spriteLocation: node.pos });
- sprite.origin = group.origin.clone();
- res.sprite = sprite;
- }
- }
- return res;
- };
- /**
- * Get a Solid Color Brush instance matching the given color.
- * @param color The color to retrieve
- * @return A shared instance of the SolidColorBrush2D class that use the given color
- */
- Canvas2D.GetSolidColorBrush = function (color) {
- return Canvas2D._solidColorBrushes.getOrAddWithFactory(color.toHexString(), function () { return new BABYLON.SolidColorBrush2D(color.clone(), true); });
- };
- /**
- * Get a Solid Color Brush instance matching the given color expressed as a CSS formatted hexadecimal value.
- * @param color The color to retrieve
- * @return A shared instance of the SolidColorBrush2D class that uses the given color
- */
- Canvas2D.GetSolidColorBrushFromHex = function (hexValue) {
- return Canvas2D._solidColorBrushes.getOrAddWithFactory(hexValue, function () { return new BABYLON.SolidColorBrush2D(BABYLON.Color4.FromHexString(hexValue), true); });
- };
- Canvas2D.GetGradientColorBrush = function (color1, color2, translation, rotation, scale) {
- if (translation === void 0) { translation = BABYLON.Vector2.Zero(); }
- if (rotation === void 0) { rotation = 0; }
- if (scale === void 0) { scale = 1; }
- return Canvas2D._gradientColorBrushes.getOrAddWithFactory(BABYLON.GradientColorBrush2D.BuildKey(color1, color2, translation, rotation, scale), function () { return new BABYLON.GradientColorBrush2D(color1, color2, translation, rotation, scale, true); });
- };
- /**
- * In this strategy only the direct children groups of the Canvas will be cached, their whole content (whatever the sub groups they have) into a single bitmap.
- * This strategy doesn't allow primitives added directly as children of the Canvas.
- * You typically want to use this strategy of a screenSpace fullscreen canvas: you don't want a bitmap cache taking the whole screen resolution but still want the main contents (say UI in the topLeft and rightBottom for instance) to be efficiently cached.
- */
- Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS = 1;
- /**
- * In this strategy each group will have its own cache bitmap (except if a given group explicitly defines the DONTCACHEOVERRIDE or CACHEINPARENTGROUP behaviors).
- * This strategy is typically used if the canvas has some groups that are frequently animated. Unchanged ones will have a steady cache and the others will be refreshed when they change, reducing the redraw operation count to their content only.
- * When using this strategy, group instances can rely on the DONTCACHEOVERRIDE or CACHEINPARENTGROUP behaviors to minimize the amount of cached bitmaps.
- * Note that in this mode the Canvas itself is not cached, it only contains the sprites of its direct children group to render, there's no point to cache the whole canvas, sprites will be rendered pretty efficiently, the memory cost would be too great for the value of it.
- */
- Canvas2D.CACHESTRATEGY_ALLGROUPS = 2;
- /**
- * In this strategy the whole canvas is cached into a single bitmap containing every primitives it owns, at the exception of the ones that are owned by a group having the DONTCACHEOVERRIDE behavior (these primitives will be directly drawn to the viewport at each render for screenSpace Canvas or be part of the Canvas cache bitmap for worldSpace Canvas).
- */
- Canvas2D.CACHESTRATEGY_CANVAS = 3;
- /**
- * This strategy is used to recompose/redraw the canvas entirely at each viewport render.
- * Use this strategy if memory is a concern above rendering performances and/or if the canvas is frequently animated (hence reducing the benefits of caching).
- * Note that you can't use this strategy for WorldSpace Canvas, they need at least a top level group caching.
- */
- Canvas2D.CACHESTRATEGY_DONTCACHE = 4;
- Canvas2D._interInfo = new BABYLON.IntersectInfo2D();
- /**
- * Define the default size used for both the width and height of a MapTexture to allocate.
- * Note that some MapTexture might be bigger than this size if the first node to allocate is bigger in width or height
- */
- Canvas2D._groupTextureCacheSize = 1024;
- Canvas2D._solidColorBrushes = new BABYLON.StringDictionary();
- Canvas2D._gradientColorBrushes = new BABYLON.StringDictionary();
- Canvas2D = __decorate([
- BABYLON.className("Canvas2D")
- ], Canvas2D);
- return Canvas2D;
- })(BABYLON.Group2D);
- BABYLON.Canvas2D = Canvas2D;
- })(BABYLON || (BABYLON = {}));
|