@sapui5/sap.ui.vbm 1.103.0 → 1.104.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -29,9 +29,9 @@ sap.ui.define([
29
29
  *
30
30
  * @private
31
31
  * @author SAP SE
32
- * @version 1.103.0
32
+ * @version 1.104.0
33
33
  * @alias sap.ui.vbm.adapter3d.ModelHandler
34
- */
34
+ */
35
35
  var ModelHandler = BaseObject.extend("sap.ui.vbm.adapter3d.ModelHandler", /** @lends sap.ui.vbm.adapter3d.ModelHandler.prototype */ {
36
36
 
37
37
  constructor: function(resources, textures, scene, root) {
@@ -94,11 +94,14 @@ sap.ui.define([
94
94
  this._textures = null;
95
95
  this._scene = null;
96
96
  this._root = null;
97
+ this._hotInstance = null;
97
98
 
98
99
  // destroy 'meta' meshes
99
- this._meshes.forEach(function(mesh) {
100
- mesh.objects3D.forEach(function(object) {
101
- this._deleteObject3D(object);
100
+ this._meshes.forEach(function(array) {
101
+ array.forEach(function(item) {
102
+ item.objects3D.forEach(function(object) {
103
+ this._deleteObject3D(object);
104
+ }, this);
102
105
  }, this);
103
106
  }, this);
104
107
 
@@ -108,15 +111,23 @@ sap.ui.define([
108
111
  subRef(instance.texture);
109
112
  }
110
113
  });
111
-
114
+
112
115
  // destroy models
113
116
  this._models.forEach(function(model, resource) {
114
117
  this._deleteModel(model);
115
118
  }, this);
116
119
 
117
120
  this._meshes.clear();
121
+ this._meshes = null;
122
+
118
123
  this._instances.clear();
124
+ this._instances = null;
125
+
119
126
  this._models.clear();
127
+ this._models = null;
128
+
129
+ this._glTFLoader = null;
130
+ this._colladaLoader = null;
120
131
 
121
132
  BaseObject.prototype.destroy.call(this);
122
133
  };
@@ -146,7 +157,7 @@ sap.ui.define([
146
157
  * @param {object} instance Model instance.
147
158
  * @public
148
159
  */
149
- ModelHandler.prototype.updateInstance = function(instance) {
160
+ ModelHandler.prototype.updateInstance = function(instance) {
150
161
  var data = this._instances.get(instance), update = false, hot = this._hotInstance && this._hotInstance === instance;
151
162
  if (data) {
152
163
  var normalized = Utilities.toBoolean(instance.normalize);
@@ -185,7 +196,7 @@ sap.ui.define([
185
196
  }
186
197
  update = true;
187
198
  }
188
-
199
+
189
200
  // when model normalization changed -> update model properties -> reset instance model matrices
190
201
  if (propertyChanged(instance, ["normalize", "model"])) {
191
202
  this._updateModel(data.model, normalized);
@@ -217,9 +228,9 @@ sap.ui.define([
217
228
  if (data.matrices.length === 0) {
218
229
  var world = this._root.matrixWorld.clone(); // start from the _root
219
230
  world.multiply(data.world); // apply instance world matrix
220
-
231
+
221
232
  // if instance normalized -> apply normalized model root matrix as otherwise (non normalized case) model root has identity matrix
222
- if (normalized) {
233
+ if (normalized) {
223
234
  world.multiply(data.model.normalized.world);
224
235
  }
225
236
  // apply individual mesh matrices from the model at last
@@ -253,7 +264,7 @@ sap.ui.define([
253
264
  this._instances.delete(instance);
254
265
  this._removeInstanceFromMesh(data);
255
266
  instance._last = {}; // reset all LRU variables at once
256
-
267
+
257
268
  if (data.model) {
258
269
  subRef(data.model); // model can be null if removing 'broken' instance
259
270
  }
@@ -428,7 +439,7 @@ sap.ui.define([
428
439
  ModelHandler.prototype._postprocess = function(model, content) {
429
440
  // mirror on Z axis entire collada root which is effectively the same as collada processing with baking transformations and inverting Z coordinates in ActiveX
430
441
  model.scene.scale.set(1,1,-1);
431
-
442
+
432
443
  var meshes = [], materials = new Set(), marked = "_sapUsed";
433
444
  // collect meshes only + calculate world matrix, mark used materials & maps
434
445
  model.scene.traverse(function(object) {
@@ -558,7 +569,7 @@ sap.ui.define([
558
569
  // remove instanced mesh from scene
559
570
  if (object.parent) {
560
571
  object.parent.remove(object);
561
- }
572
+ }
562
573
  // geometries are shared -> cannot dispose
563
574
  Utilities.toArray(object.material).forEach(function(material) {
564
575
  material.dispose(); // materials are cloned -> can be disposed, maps are shared -> don't touch, cannot be disposes
@@ -582,7 +593,7 @@ sap.ui.define([
582
593
  // target has to be added to _root to mimic original model and must have same world matrix as original model
583
594
  // so we calculate world matrix for target which will be under _root and not under _scene as instance mesh
584
595
  var world = data.world.clone(); // use instance world as starting point
585
- if (data.model.normalized) {
596
+ if (data.model.normalized) {
586
597
  world.multiply(data.model.normalized.world);
587
598
  }
588
599
  world.multiply(data.model.root.children[0].matrixWorld);
@@ -377,7 +377,7 @@ sap.ui.define([
377
377
  *
378
378
  * @private
379
379
  * @author SAP SE
380
- * @version 1.103.0
380
+ * @version 1.104.0
381
381
  * @alias sap.ui.vbm.adapter3d.ObjectFactory
382
382
  */
383
383
  var ObjectFactory = BaseObject.extend("sap.ui.vbm.adapter3d.ObjectFactory", /** @lends sap.ui.vbm.adapter3d.ObjectFactory.prototype */ {});
@@ -32,9 +32,9 @@ sap.ui.define([
32
32
  *
33
33
  * @private
34
34
  * @author SAP SE
35
- * @version 1.103.0
35
+ * @version 1.104.0
36
36
  * @alias sap.ui.vbm.adapter3d.PolygonHandler
37
- */
37
+ */
38
38
  var PolygonHandler = BaseObject.extend("sap.ui.vbm.adapter3d.PolygonHandler", /** @lends sap.ui.vbm.adapter3d.PolygonHandler.prototype */ {
39
39
 
40
40
  constructor: function(root) {
@@ -97,7 +97,7 @@ sap.ui.define([
97
97
  PolygonHandler.prototype.destroy = function() {
98
98
  // Reset references to shared objects.
99
99
  this._root = null;
100
- this._instances.clear();
100
+ this._hotInstance = null;
101
101
 
102
102
  // destroy 'meta' meshes
103
103
  this._meshes.forEach(function(key, array) {
@@ -112,10 +112,16 @@ sap.ui.define([
112
112
  this._deleteObject3D(border.object3D);
113
113
  }, this);
114
114
  }, this);
115
-
115
+
116
116
  // clear
117
+ this._instances.clear();
118
+ this._instances = null;
119
+
117
120
  this._meshes.clear();
121
+ this._meshes = null;
122
+
118
123
  this._borders.clear();
124
+ this._borders = null;
119
125
 
120
126
  BaseObject.prototype.destroy.call(this);
121
127
  };
@@ -135,11 +141,11 @@ sap.ui.define([
135
141
 
136
142
  /**
137
143
  * Adds polygon instance to polygon handler.
138
- *
144
+ *
139
145
  * @param {object} instance Polygon instance.
140
146
  * @public
141
147
  */
142
- PolygonHandler.prototype.addInstance = function(instance) {
148
+ PolygonHandler.prototype.addInstance = function(instance) {
143
149
  this._instances.set(instance, {
144
150
  instance : instance,
145
151
  indices: [],
@@ -159,11 +165,11 @@ sap.ui.define([
159
165
 
160
166
  /**
161
167
  * Updates polygon instance in polygon handler
162
- *
168
+ *
163
169
  * @param {object} instance Polygon instance.
164
170
  * @public
165
171
  */
166
- PolygonHandler.prototype.updateInstance = function(instance) {
172
+ PolygonHandler.prototype.updateInstance = function(instance) {
167
173
  var data = this._instances.get(instance), updateMesh = false, updateBorder = false;
168
174
  if (data) {
169
175
  // position, rotation, scale [mandatory attributes] when change -> recalculate matrix
@@ -186,7 +192,7 @@ sap.ui.define([
186
192
  }
187
193
 
188
194
  // if any property which affects polygon color changed -> check if polygon color actually changed
189
- // then update polygon color and remove polygon from current 'meta' mesh if it exists
195
+ // then update polygon color and remove polygon from current 'meta' mesh if it exists
190
196
  // and put it into another 'meta' mesh according to polygon updated color
191
197
  // color, selectColor, VB:s [mandatory attributes]
192
198
  if (propertyChanged(instance, ["color", "selectColor", "VB:s"])) {
@@ -327,13 +333,13 @@ sap.ui.define([
327
333
  // create 3 or 2 groups, depends on where hot instance geometry is within 'meta' mesh geometry
328
334
  geometry.addGroup(range.start, data.indices.length, 1); // use hot material with index #1
329
335
 
330
- if (range.start !== 0) {
336
+ if (range.start !== 0) {
331
337
  geometry.addGroup(0, range.start, 0); // use original material with index #0
332
338
  }
333
339
  if (range.start + data.indices.length < geometry.index.count) {
334
340
  geometry.addGroup(range.start + data.indices.length, geometry.index.count - range.start - data.indices.length, 0); // use original material with index #0
335
341
  }
336
- }
342
+ }
337
343
  }
338
344
  if (data.border) { // apply hover on polygon border geometry
339
345
  material = data.border.material;
@@ -363,13 +369,13 @@ sap.ui.define([
363
369
  var vertexCount = data.lines.length/3;
364
370
  geometry.addGroup(range.start, vertexCount, 1); // use hot material with index #1
365
371
 
366
- if (range.start !== 0) {
372
+ if (range.start !== 0) {
367
373
  geometry.addGroup(0, range.start, 0); // use original material with index #0
368
374
  }
369
375
  if (range.start + vertexCount < positions.count) {
370
376
  geometry.addGroup(range.start + vertexCount, positions.count - range.start - vertexCount, 0); // use original material with index #0
371
377
  }
372
- }
378
+ }
373
379
  }
374
380
  } else {
375
381
  Log.error("Unable to find polygon instance data", "", thisModule);
@@ -428,7 +434,7 @@ sap.ui.define([
428
434
 
429
435
  /**
430
436
  * Updates 'meta' meshes requested for rebuild by combining geometry of all instances with the same color into one mesh.
431
- *
437
+ *
432
438
  * @returns {boolean} True if hot instance 'meta' mesh has been recreated.
433
439
  * @private
434
440
  */
@@ -439,11 +445,11 @@ sap.ui.define([
439
445
  for (var i = 0; i < array.length;) {
440
446
  if (array[i].dirty) {
441
447
  var j, instance, base, indices = [], vertices = [], normals = [], data = array[i];
442
-
448
+
443
449
  this._deleteObject3D(data.object3D);
444
450
  data.object3D = null;
445
451
  data.hitInfo.length = 0; // erase hitInfo
446
-
452
+
447
453
  data.instances.forEach(function(range, key) {
448
454
  instance = key; // to access instance outside forEach
449
455
 
@@ -479,24 +485,24 @@ sap.ui.define([
479
485
 
480
486
  if (indices.length) {
481
487
  var geometry = new THREE.BufferGeometry();
482
-
488
+
483
489
  geometry.setIndex(indices);
484
490
  geometry.setAttribute("position", new THREE.Float32BufferAttribute(vertices, 3));
485
491
  geometry.setAttribute("normal", new THREE.Float32BufferAttribute(normals, 3));
486
492
  geometry.computeBoundingBox(); // needed for ray casting
487
493
  geometry.computeBoundingSphere(); // needed for ray casting
488
-
494
+
489
495
  var material = createMaterial(true);
490
496
  material.color.copy(instance.color.rgb);
491
497
  material.opacity = instance.color.opacity;
492
498
  material.transparent = material.opacity < 1;
493
499
  material.needsUpdate = true;
494
-
500
+
495
501
  data.object3D = new THREE.Mesh(geometry, material);
496
502
  data.object3D.matrixAutoUpdate = false;
497
503
  data.object3D.layers.set(0); // put it to layer #0 to enable raycasting
498
504
  this._root.add(data.object3D);
499
-
505
+
500
506
  data.object3D._instanceHitTest = this._instanceHitTest.bind(data); // helper function to return instance based on hit test info
501
507
  data.triangleCount = indices.length/3; // update triangle count on full rebuild
502
508
  }
@@ -527,7 +533,7 @@ sap.ui.define([
527
533
 
528
534
  /**
529
535
  * Updates 'meta' borders requested for rebuild by combining geometry of all instances with the same color into one mesh.
530
- *
536
+ *
531
537
  * @returns {boolean} True if hot instance 'meta' border has been recreated.
532
538
  * @private
533
539
  */
@@ -565,13 +571,13 @@ sap.ui.define([
565
571
  var geometry = new THREE.BufferGeometry();
566
572
  geometry.setAttribute("position", new THREE.Float32BufferAttribute(vertices, 3));
567
573
  geometry.computeBoundingBox();
568
-
574
+
569
575
  var material = createLineMaterial();
570
576
  material.color.copy(instance.colorBorder.rgb);
571
577
  material.opacity = instance.colorBorder.opacity;
572
578
  material.transparent = material.opacity < 1;
573
579
  material.needsUpdate = true;
574
-
580
+
575
581
  data.object3D = new THREE.LineSegments(geometry, material);
576
582
  data.object3D.matrixAutoUpdate = false;
577
583
  data.object3D.layers.set(1); // put it to layer #1 to disable hit test
@@ -739,10 +745,10 @@ sap.ui.define([
739
745
  if (object) {
740
746
  if (object.parent) {
741
747
  object.parent.remove(object);
742
- }
748
+ }
743
749
  if (object.geometry) {
744
750
  object.geometry.dispose();
745
- }
751
+ }
746
752
  Utilities.toArray(object.material).forEach(function(material) {
747
753
  material.dispose(); // no maps to dispose
748
754
  });
@@ -46,7 +46,7 @@ sap.ui.define([
46
46
  *
47
47
  * @private
48
48
  * @author SAP SE
49
- * @version 1.103.0
49
+ * @version 1.104.0
50
50
  * @alias sap.ui.vbm.adapter3d.SceneBuilder
51
51
  */
52
52
  var SceneBuilder = BaseObject.extend("sap.ui.vbm.adapter3d.SceneBuilder", /** @lends sap.ui.vbm.adapter3d.SceneBuilder.prototype */ {
@@ -72,9 +72,12 @@ sap.ui.define([
72
72
  // Decal helper object
73
73
  this._decalHelper = null;
74
74
 
75
- // instance key -> instance map, for targeted objects like Decal
75
+ // instance key -> instance, for targeted objects like Decal
76
76
  this._targets = new Map();
77
77
 
78
+ // instance -> list of Decal instances targeted it
79
+ this._instanceDecals = new Map();
80
+
78
81
  // Cached objects. THREE.Texture is reference countable.
79
82
  this._textures = new Map(); //texture name -> THREE.Texture
80
83
 
@@ -103,9 +106,11 @@ sap.ui.define([
103
106
  this._scene = null;
104
107
  this._viewport = null;
105
108
  this._context = null;
109
+ this._hotInstance = null;
110
+
106
111
 
107
112
  if (this._box4) {
108
- this._box.dispose();
113
+ this._box4.dispose();
109
114
  }
110
115
  if (this._box6) {
111
116
  this._box6.dispose();
@@ -121,15 +126,25 @@ sap.ui.define([
121
126
  this._decalHelper.material.dispose();
122
127
  this._decalHelper.geometry.dispose();
123
128
  this._scene.remove(this._decalHelper);
129
+ this._decalHelper = null;
124
130
  }
125
131
 
126
132
  this._polygonHandler.destroy();
133
+ this._polygonHandler = null;
134
+
127
135
  this._modelHandler.destroy();
136
+ this._modelHandler = null;
128
137
 
129
138
  this._textures.forEach(function(texture) {
130
139
  texture.dispose();
131
140
  });
132
141
 
142
+ this._targets.clear();
143
+ this._targets = null;
144
+
145
+ this._instanceDecals.clear();
146
+ this._instanceDecals = null;
147
+
133
148
  BaseObject.prototype.destroy.call(this);
134
149
  };
135
150
 
@@ -468,17 +483,10 @@ sap.ui.define([
468
483
  this._polygonHandler.addInstance(instance);
469
484
  } else if (instance.isModel) {
470
485
  this._modelHandler.addInstance(instance);
471
- } else if (instance.isBox || instance.isCylinder) {
472
- // common part for Box & Cylinder
473
- instance.object3D = new THREE.Group();
474
- instance.object3D.matrixAutoUpdate = false;
475
- this._root.add(instance.object3D);
476
- // assign instance type specific properties
477
- if (instance.isBox) {
478
- this._assignBoxProperties(instance);
479
- } else {
480
- this._assignCylinderProperties(instance);
481
- }
486
+ } else if (instance.isBox) {
487
+ this._assignBoxProperties(instance);
488
+ } else if (instance.isCylinder) {
489
+ this._assignCylinderProperties(instance);
482
490
  } else if (instance.isDecal) {
483
491
  if (decals) {
484
492
  decals.push(instance); // first pass -> keep decal for the second pass
@@ -507,7 +515,7 @@ sap.ui.define([
507
515
  if (decals) {
508
516
  decals.push(instance); // first pass -> keep decal for the second pass
509
517
  } else {
510
- this._assignDecalProperties(instance); // second padd -> update decal
518
+ this._assignDecalProperties(instance); // second pass -> update decal
511
519
  }
512
520
  } else {
513
521
  Log.error("Unable to update instance: instance type is unknown", "", thisModule);
@@ -528,6 +536,16 @@ sap.ui.define([
528
536
  this._deleteObject3D(instance.object3D);
529
537
  instance.object3D = null;
530
538
  instance._last = {}; // reset all LRU variables at once
539
+
540
+ // update (instance <-> decals) map if decal has target
541
+ if (instance.isDecal && instance.target) {
542
+ var target = this._targets.get(instance.target);
543
+ if (target) {
544
+ this._instanceDecals.get(target).delete(instance);
545
+ } else {
546
+ Log.error("Unable to remove decal from (instance <-> decals) map", "", thisModule);
547
+ }
548
+ }
531
549
  } else {
532
550
  Log.error("Unable to remove instance: object3D is missing", "", thisModule);
533
551
  }
@@ -595,7 +613,7 @@ sap.ui.define([
595
613
  };
596
614
 
597
615
  SceneBuilder.prototype._assignBoxProperties = function(instance, hot) {
598
- var box = instance.object3D.children.length === 0 ? null : instance.object3D.children[0];
616
+ var box = instance.object3D || null;
599
617
 
600
618
  // geometry has to be created initially or recreated when 'texture6' changed
601
619
  if (!box || propertyChanged(instance, "texture6")) {
@@ -610,7 +628,8 @@ sap.ui.define([
610
628
  box.matrixAutoUpdate = false;
611
629
  box.layers.set(0); // put it to layer #0 to enable raycasting
612
630
  box._sapInstance = instance; // keep reference to instance
613
- instance.object3D.add(box);
631
+ this._root.add(box);
632
+ instance.object3D = box;
614
633
  }
615
634
  }
616
635
 
@@ -622,7 +641,7 @@ sap.ui.define([
622
641
  };
623
642
 
624
643
  SceneBuilder.prototype._assignCylinderProperties = function(instance, hot) {
625
- var material, updateTextureCap = false, open = toBoolean(instance.isOpen), cylinder = instance.object3D.children.length === 0 ? null : instance.object3D.children[0];
644
+ var material, updateTextureCap = false, open = toBoolean(instance.isOpen), cylinder = instance.object3D || null;
626
645
 
627
646
  // geometry has to be created initially or recreated when 'isOpen' changed
628
647
  if (!cylinder || propertyChanged(instance, "isOpen")) {
@@ -666,7 +685,8 @@ sap.ui.define([
666
685
  cylinder.matrixAutoUpdate = false;
667
686
  cylinder.layers.set(0); // put it to layer #0 to enable raycasting
668
687
  cylinder._sapInstance = instance; // keep reference to instance
669
- instance.object3D.add(cylinder);
688
+ this._root.add(cylinder);
689
+ instance.object3D = cylinder;
670
690
  }
671
691
  }
672
692
 
@@ -692,14 +712,14 @@ sap.ui.define([
692
712
  }
693
713
 
694
714
  // update cylinder properties after processing is done
695
- updateProperty(instance, ["isOpen", "testureCap"]);
715
+ updateProperty(instance, ["isOpen", "textureCap"]);
696
716
 
697
717
  // handle common properties
698
718
  this._assignProperties(instance, hot);
699
719
  };
700
720
 
701
721
  SceneBuilder.prototype._assignProperties = function(instance, hot) {
702
- var color, material, mesh = instance.object3D.children[0];
722
+ var color, material, mesh = instance.object3D;
703
723
 
704
724
  if (propertyChanged(instance, "texture")) {
705
725
  material = Array.isArray(mesh.material) ? mesh.material[0] : mesh.material;
@@ -756,22 +776,23 @@ sap.ui.define([
756
776
  }
757
777
  }
758
778
 
759
- // when normalize property changed -> perform object node normalization or reset it's transformation to default
760
- if (propertyChanged(instance, "normalize")) {
779
+ // if any of the following properties changed -> recalculate transform matrix & normalize matrix -> decompose it and assign to the mesh
780
+ if (propertyChanged(instance, ["pos", "rot", "scale", "normalize"])) {
781
+ var normalizeMatrix = new Matrix4(), transformMatrix = new Matrix4();
761
782
  if (toBoolean(instance.normalize)) {
762
- Utilities.normalizeObject3D(mesh);
763
- mesh.updateMatrix(); // important to update local TM once transformation attributes have changed
764
- } else {
765
783
  mesh.position.set(0, 0, 0);
766
784
  mesh.rotation.set(0, 0, 0);
767
785
  mesh.scale.set(1, 1, 1);
786
+ mesh.updateMatrix();
787
+ // reset local transform first as it affects normalization
788
+ Utilities.normalizeObject3D(mesh, normalizeMatrix);
768
789
  }
769
- }
790
+ Utilities.getInstanceMatrix(instance, transformMatrix);
791
+
792
+ transformMatrix.multiply(normalizeMatrix);
793
+ transformMatrix.decompose(mesh.position, mesh.quaternion, mesh.scale);
770
794
 
771
- // position, rotation, scale [mandatory attributes] when change -> recalculate transformation & update matrix
772
- if (propertyChanged(instance, ["pos", "rot", "scale"])) {
773
- Utilities.getInstanceTransform(instance, instance.object3D.position, instance.object3D.rotation, instance.object3D.scale);
774
- instance.object3D.updateMatrix(); // important to update local TM once transformation attributes have changed
795
+ mesh.updateMatrix(); // important to update local TM once transformation attributes have changed
775
796
  }
776
797
 
777
798
  // update properties after processing is done
@@ -831,6 +852,21 @@ sap.ui.define([
831
852
  }
832
853
  }
833
854
 
855
+ // update (instance <-> decals) map
856
+ if (propertyChanged(instance, "target")) {
857
+ var targetInstance = this._targets.get(instance._last.target);
858
+ if (targetInstance) {
859
+ this._instanceDecals.get(targetInstance).delete(instance);
860
+ }
861
+ targetInstance = this._targets.get(instance.target);
862
+ if (targetInstance) {
863
+ if (!this._instanceDecals.has(targetInstance)) {
864
+ this._instanceDecals.set(targetInstance, new Set());
865
+ }
866
+ this._instanceDecals.get(targetInstance).add(instance);
867
+ }
868
+ }
869
+
834
870
  // update properties after processing is done
835
871
  updateProperty(instance, ["position", "direction", "size", "rotation", "target", "texture", "text", "planeOrigin", "planeNormal"]);
836
872
  };
@@ -1019,7 +1055,7 @@ sap.ui.define([
1019
1055
  var target = this._targets.get(instance.target);
1020
1056
  if (target) {
1021
1057
  if (target.isBox || target.isCylinder) {
1022
- return target.object3D.children[0]; // object3d is a group node, where its first child is actual mesh
1058
+ return target.object3D;
1023
1059
  } else if (target.isPolygon) {
1024
1060
  return this._polygonHandler.getTarget(target);
1025
1061
  } else if (target.isModel) {
@@ -1055,8 +1091,16 @@ sap.ui.define([
1055
1091
  SceneBuilder.prototype._updateInstanceKeys = function(instance) {
1056
1092
  var value = this._getInstanceKeys(instance);
1057
1093
  if (value) {
1058
- this._targets.set(value.key, instance);
1059
- this._targets.set(value.group, instance);
1094
+ if (value.key !== instance._last.key) {
1095
+ this._targets.delete(instance._last.key);
1096
+ this._targets.delete(instance._last.group);
1097
+
1098
+ this._targets.set(value.key, instance);
1099
+ this._targets.set(value.group, instance);
1100
+
1101
+ instance._last.key = value.key;
1102
+ instance._last.group = value.group;
1103
+ }
1060
1104
  }
1061
1105
  };
1062
1106
 
@@ -80,13 +80,13 @@ sap.ui.define([
80
80
  };
81
81
 
82
82
  // from threeJS to VB vector (convert from Right Handed [GL] to Left Handed [DX] coordinate system)
83
- Utilities.threeJsToVb = function(point) {
84
- return new Vector3(-point.x, point.z, -point.y);
83
+ Utilities.threeJsToVb = function(point, out) {
84
+ return out ? out.set(-point.x, point.z, -point.y) : new Vector3(-point.x, point.z, -point.y);
85
85
  };
86
86
 
87
87
  // from VB to ThreeJS vector (convert from Left Handed [DX] to Right Handed [GL] coordinate system)
88
- Utilities.vbToThreeJs = function(point) {
89
- return new Vector3(-point.x, -point.z, point.y);
88
+ Utilities.vbToThreeJs = function(point, out) {
89
+ return out ? out.set(-point.x, -point.z, point.y) : new Vector3(-point.x, -point.z, point.y);
90
90
  };
91
91
 
92
92
  Utilities.toColor = (function() {
@@ -550,5 +550,16 @@ sap.ui.define([
550
550
  return undefined;
551
551
  };
552
552
 
553
+ /**
554
+ * Function checks whether channel is enabled in layers object
555
+ *
556
+ * @param {THREE.Layers} layers The Layers object
557
+ * @param {number} channel The channel number to test
558
+ * @returns {boolean} True if channel is enabled in Layers objects or false otherwise
559
+ */
560
+ Utilities.layersEnabled = function(layers, channel) {
561
+ return (layers.mask & ( 1 << channel | 0 ) ) !== 0;
562
+ };
563
+
553
564
  return Utilities;
554
565
  });