@woosh/meep-engine 2.84.9 → 2.84.10
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.
- package/README.md +27 -13
- package/build/meep.cjs +185 -87
- package/build/meep.min.js +1 -1
- package/build/meep.module.js +185 -87
- package/editor/process/symbolic/makePositionedIconDisplaySymbol.js +2 -4
- package/editor/view/EditorView.js +48 -204
- package/editor/view/ecs/HierarchicalEntityListView.js +191 -0
- package/editor/view/ecs/HierarchicalEntityListView.module.scss +13 -0
- package/editor/view/prepareMeshLibrary.js +178 -0
- package/editor/view/v2/SplitView.js +104 -0
- package/editor/view/v2/ViewManagementSystem.js +0 -0
- package/editor/view/v2/prototypeEditor.js +127 -0
- package/package.json +1 -1
- package/src/core/cache/Cache.d.ts +2 -0
- package/src/core/cache/Cache.js +58 -8
- package/src/core/cache/Cache.spec.js +38 -0
- package/src/core/cache/CacheElement.js +6 -0
- package/src/core/cache/LoadingCache.js +25 -2
- package/src/core/cache/LoadingCache.spec.js +9 -6
- package/src/core/collection/array/arraySetSortingDiff.js +6 -6
- package/src/core/collection/table/RowFirstTable.js +364 -368
- package/src/core/geom/3d/plane/plane3_compute_ray_intersection.js +3 -1
- package/src/core/geom/3d/topology/simplify/prototypeMeshSimplification.js +7 -7
- package/src/engine/animation/curve/ecd_bind_animation_curve.js +9 -0
- package/src/engine/graphics/ecs/mesh-v2/ShadedGeometryFlags.js +8 -1
- package/src/engine/graphics/ecs/mesh-v2/aggregate/prototypeSGMesh.js +23 -19
- package/src/engine/graphics/ecs/mesh-v2/sg_hierarchy_compute_bounding_box_via_parent_entity.js +2 -2
- package/src/engine/graphics/ecs/mesh-v2/three_object_to_entity_composition.js +3 -1
- package/src/view/View.js +64 -95
- package/src/view/setElementTransform.js +20 -0
- package/src/view/setElementVisibility.js +15 -0
package/README.md
CHANGED
|
@@ -15,14 +15,24 @@ To help get you started, various samples are provided under `/samples` folder. F
|
|
|
15
15
|
|
|
16
16
|
## Features
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
### Automatic instancing
|
|
19
|
+
This will minimize the number of draw calls and improve performance.
|
|
20
|
+
### Decals
|
|
21
|
+
Decals in meep are done on the GPU, and as a result you can have a lot of them in your scene. The most I tested in a single scene is 1,000,000 which works quite well. Decals are useful for bullet holes, blood splatter and various scene decorations such as signs and scuff marks.
|
|
22
|
+
### Clustered lighting, aka Forward+
|
|
23
|
+
Meep implements clustered lighting technique which allows you to have a pretty much an unlimited number of point lights in your scene. All the other light types are supported as well, but only point lights are clustered for now. Point lights are useful for various effects, like muzzle flashes, grenade explosions, torches, etc.
|
|
24
|
+
### Particles
|
|
25
|
+
Meep's particle engine ("Particular") is optimized for game development needs, this means supporting complex effects and being able to spawn/destroy many particle systems every frame. There are a lot of particle engines out there, but they tend to have costly spawning routines, take up a lot of draw calls and not manage memory well, which leads to pretty poor performance in games. My particle engine supports full particle lighting as well, and soft particles. On top of that - all particles are automatically atlassed in the background and are compiled with just 4 shaders. This means that no matter how many particle effects you have - there will be no shader switching and no texture switching, and there will be no delays associated with shader compilation. Particles are culled, so particle systems that are off-screen are not rendered and simulation for them can be paused to save CPU resources (this is automatic behavior)
|
|
26
|
+
### Trails
|
|
27
|
+
Meep implements a fairly complex trail system, where a trail can be attached to an entity, and it will create a trail behind.
|
|
28
|
+
### Terrain
|
|
29
|
+
The terrain engine is chunk-based, which means that terrain is split into rectangular pieces internally, and they are built on-the-fly inside a web-worker based on camera position. Terrain is automatically culled based on camera position, so you're only drawing the chunks that are in view. Terrain supports layers just like Unity, but unlike Unity - Meep supports up to 256 layers of terrain instead of 4.
|
|
30
|
+
### Sound engine
|
|
31
|
+
Meep has a custom sound engine which is culled and has custom attenuation, this allows scenes to have 1000s of positional audio sources without any extra cost in terms of performance
|
|
32
|
+
### Asset streaming
|
|
33
|
+
Meep in general is a web-first engine, all assets are streamed, meaning that the engine is up and running even before any models/texture are loaded, and you're free to decide when to let the player see your level, wait until everything is loaded or drop them in as soon as you can. There is a pre-loader module in case you want to load some assets in bulk before starting the game.
|
|
34
|
+
### AI tools
|
|
35
|
+
including, but not limited to:
|
|
26
36
|
* full-fledged Behavior trees
|
|
27
37
|
* Blackboard (took for recording information useful for AI to read/write relevant world state, very commonly used with Behavior Trees)
|
|
28
38
|
* monte-carlo tree search (same as Google's AlphaGo, useful for decision-making)
|
|
@@ -30,11 +40,15 @@ To help get you started, various samples are provided under `/samples` folder. F
|
|
|
30
40
|
* grid-based path finding (optimized for 10,000s of queries per second)
|
|
31
41
|
* resource allocation solver (useful for planning, given certain resources such as bullets/grenades/money/health - plan what actions to take to optimize your chances of winning)
|
|
32
42
|
* and a number of other useful tools to complement AI development.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
43
|
+
### Inverse kinematics
|
|
44
|
+
Meep has 2 very useful IK solvers to fix foot position for characters on uneven terrain or stairs, aligning both the position and orientation of character feet. This works for hands as well if you want and is not limited to bipeds, the system works just as well for, say, spiders.
|
|
45
|
+
### High-performance serialization
|
|
46
|
+
Extremely compact serialization system. You can save/load your scenes from files. This serialization system supports format changes, so as you develop your game - old saves will be supported and the system will automatically upgrade them to the most recent version, so the player doesn't have to lose any data.
|
|
47
|
+
### Achievements
|
|
48
|
+
Achievements work with Blackboard components and are very easy to define. There is an abstraction system that helps connect your achievements to various platforms such as Steam or XBox (you'd have to implement that binding yourself though), by default it comes with a browser backend, storing data in IndexedDB. I also have bindings for Steam and NewGrounds that I can share.
|
|
49
|
+
### UI system
|
|
50
|
+
You're free to use it or not, the engine works just fine without it. The system is written for speed, and it offers a few commonly used tools, such as popup windows, notifications and pages. The UI system is optimized for speed, so it generates no garbage, but again - if you want to use, say React - you can ignore this system or integrate it with the system.
|
|
51
|
+
|
|
38
52
|
|
|
39
53
|
For more information, please refer to the documentation
|
|
40
54
|
|
package/build/meep.cjs
CHANGED
|
@@ -60580,6 +60580,12 @@ class CacheElement {
|
|
|
60580
60580
|
*/
|
|
60581
60581
|
this.value = null;
|
|
60582
60582
|
|
|
60583
|
+
/**
|
|
60584
|
+
*
|
|
60585
|
+
* @type {number}
|
|
60586
|
+
*/
|
|
60587
|
+
this.weight = 0;
|
|
60588
|
+
|
|
60583
60589
|
/**
|
|
60584
60590
|
* Link to next element (implements linked list)
|
|
60585
60591
|
* @type {CacheElement<Key,Value>|null}
|
|
@@ -60768,14 +60774,56 @@ class Cache {
|
|
|
60768
60774
|
recomputeWeight() {
|
|
60769
60775
|
let result = 0;
|
|
60770
60776
|
|
|
60771
|
-
for (let [key,
|
|
60772
|
-
|
|
60773
|
-
|
|
60777
|
+
for (let [key, record] of this.data) {
|
|
60778
|
+
|
|
60779
|
+
const weight = this.computeElementWeight(key, record.value);
|
|
60780
|
+
record.weight = weight;
|
|
60781
|
+
|
|
60782
|
+
result += weight;
|
|
60774
60783
|
}
|
|
60775
60784
|
|
|
60776
60785
|
this.weight = result;
|
|
60777
60786
|
}
|
|
60778
60787
|
|
|
60788
|
+
/**
|
|
60789
|
+
* Useful when working with wrapped value types, where contents can change and affect overall weight
|
|
60790
|
+
* Instead of re-inserting element, we can just update weights
|
|
60791
|
+
* NOTE: this method may trigger eviction
|
|
60792
|
+
* @param {Key} key
|
|
60793
|
+
* @returns {boolean} true when weight successfully updated, false if element was not found in cache
|
|
60794
|
+
*/
|
|
60795
|
+
updateElementWeight(key) {
|
|
60796
|
+
const record = this.data.get(key);
|
|
60797
|
+
|
|
60798
|
+
if (record === undefined) {
|
|
60799
|
+
return false;
|
|
60800
|
+
}
|
|
60801
|
+
|
|
60802
|
+
const old_weight = record.weight;
|
|
60803
|
+
|
|
60804
|
+
const new_weight = this.computeElementWeight(key, record.value);
|
|
60805
|
+
|
|
60806
|
+
if (new_weight === old_weight) {
|
|
60807
|
+
// we're done, no change
|
|
60808
|
+
return true;
|
|
60809
|
+
}
|
|
60810
|
+
|
|
60811
|
+
record.weight = new_weight;
|
|
60812
|
+
|
|
60813
|
+
const delta_weight = new_weight - old_weight;
|
|
60814
|
+
|
|
60815
|
+
this.weight += delta_weight;
|
|
60816
|
+
|
|
60817
|
+
if (
|
|
60818
|
+
this.weight > this.maxWeight
|
|
60819
|
+
&& new_weight >= this.maxWeight //make it less likely to drop entire cache
|
|
60820
|
+
) {
|
|
60821
|
+
this.evictUntilWeight(this.maxWeight);
|
|
60822
|
+
}
|
|
60823
|
+
|
|
60824
|
+
return true;
|
|
60825
|
+
}
|
|
60826
|
+
|
|
60779
60827
|
/**
|
|
60780
60828
|
* @private
|
|
60781
60829
|
* @param {Key} key
|
|
@@ -60783,7 +60831,11 @@ class Cache {
|
|
|
60783
60831
|
* @returns {number}
|
|
60784
60832
|
*/
|
|
60785
60833
|
computeElementWeight(key, value) {
|
|
60786
|
-
|
|
60834
|
+
const key_weight = this.keyWeigher(key);
|
|
60835
|
+
|
|
60836
|
+
const value_weight = this.valueWeigher(value);
|
|
60837
|
+
|
|
60838
|
+
return key_weight + value_weight;
|
|
60787
60839
|
}
|
|
60788
60840
|
|
|
60789
60841
|
/**
|
|
@@ -60856,6 +60908,9 @@ class Cache {
|
|
|
60856
60908
|
//compute weight
|
|
60857
60909
|
const elementWeight = this.computeElementWeight(key, value);
|
|
60858
60910
|
|
|
60911
|
+
element.weight = elementWeight;
|
|
60912
|
+
|
|
60913
|
+
|
|
60859
60914
|
/**
|
|
60860
60915
|
* It's possible that element being added is larger than cache's capacity,
|
|
60861
60916
|
* in which case entire cache will be evicted, but there still won't be enough space
|
|
@@ -60949,7 +61004,7 @@ class Cache {
|
|
|
60949
61004
|
* @private
|
|
60950
61005
|
*/
|
|
60951
61006
|
__removeElement(element) {
|
|
60952
|
-
|
|
61007
|
+
element.value;
|
|
60953
61008
|
|
|
60954
61009
|
// remove from the queue
|
|
60955
61010
|
if (element === this.__first) {
|
|
@@ -60964,14 +61019,11 @@ class Cache {
|
|
|
60964
61019
|
|
|
60965
61020
|
const key = element.key;
|
|
60966
61021
|
|
|
60967
|
-
//compute weight
|
|
60968
|
-
const elementWeight = this.computeElementWeight(key, value);
|
|
60969
|
-
|
|
60970
61022
|
//remove from cache
|
|
60971
61023
|
this.data.delete(key);
|
|
60972
61024
|
|
|
60973
61025
|
//update weight
|
|
60974
|
-
this.weight -=
|
|
61026
|
+
this.weight -= element.weight;
|
|
60975
61027
|
}
|
|
60976
61028
|
|
|
60977
61029
|
/**
|
|
@@ -66277,7 +66329,14 @@ const ShadedGeometryFlags = {
|
|
|
66277
66329
|
/**
|
|
66278
66330
|
* If set to false will not render
|
|
66279
66331
|
*/
|
|
66280
|
-
Visible:16
|
|
66332
|
+
Visible: 16,
|
|
66333
|
+
|
|
66334
|
+
/**
|
|
66335
|
+
* Bounds are updated whenever transforms change, we can defer this until next frame render request
|
|
66336
|
+
* This lets us back updated and do less work overall
|
|
66337
|
+
* TODO implement, currently it's ignored
|
|
66338
|
+
*/
|
|
66339
|
+
DeferredBoundsUpdate: 32,
|
|
66281
66340
|
};
|
|
66282
66341
|
|
|
66283
66342
|
/**
|
|
@@ -68369,6 +68428,7 @@ function plane3_compute_ray_intersection(
|
|
|
68369
68428
|
const t = -p / denom;
|
|
68370
68429
|
|
|
68371
68430
|
if (t < 0) {
|
|
68431
|
+
// ray starts and points behind the plane
|
|
68372
68432
|
return false;
|
|
68373
68433
|
}
|
|
68374
68434
|
|
|
@@ -68381,9 +68441,10 @@ function plane3_compute_ray_intersection(
|
|
|
68381
68441
|
return true;
|
|
68382
68442
|
|
|
68383
68443
|
} else {
|
|
68444
|
+
// ray direction is perpendicular to the plane normal. In other words ray runs parallel to the plane
|
|
68384
68445
|
|
|
68385
68446
|
if (p === 0) {
|
|
68386
|
-
|
|
68447
|
+
// ray origin lies on the plane
|
|
68387
68448
|
out.set(originX, originY, originZ);
|
|
68388
68449
|
|
|
68389
68450
|
return true;
|
|
@@ -73586,6 +73647,39 @@ function assetTypeByPath(url) {
|
|
|
73586
73647
|
}
|
|
73587
73648
|
}
|
|
73588
73649
|
|
|
73650
|
+
class Name extends ObservedString {
|
|
73651
|
+
constructor(value = "") {
|
|
73652
|
+
super(value);
|
|
73653
|
+
}
|
|
73654
|
+
|
|
73655
|
+
/**
|
|
73656
|
+
*
|
|
73657
|
+
* @returns {string}
|
|
73658
|
+
*/
|
|
73659
|
+
getLocalizationKey() {
|
|
73660
|
+
return `component.name.${this.getValue()}`;
|
|
73661
|
+
}
|
|
73662
|
+
|
|
73663
|
+
/**
|
|
73664
|
+
*
|
|
73665
|
+
* @param {Localization} localization
|
|
73666
|
+
* @returns {string}
|
|
73667
|
+
*/
|
|
73668
|
+
getLocalizedValue(localization) {
|
|
73669
|
+
return localization.getString(this.getLocalizationKey());
|
|
73670
|
+
}
|
|
73671
|
+
|
|
73672
|
+
clone() {
|
|
73673
|
+
const clone = new Name();
|
|
73674
|
+
|
|
73675
|
+
clone.copy(this);
|
|
73676
|
+
|
|
73677
|
+
return clone;
|
|
73678
|
+
}
|
|
73679
|
+
}
|
|
73680
|
+
|
|
73681
|
+
Name.typeName = "Name";
|
|
73682
|
+
|
|
73589
73683
|
/**
|
|
73590
73684
|
*
|
|
73591
73685
|
* @param {Transform} transform
|
|
@@ -73622,6 +73716,7 @@ function three_object_to_entity_composition(root) {
|
|
|
73622
73716
|
transform.fromMatrix4(root.matrixWorld.elements);
|
|
73623
73717
|
|
|
73624
73718
|
entity.add(transform);
|
|
73719
|
+
entity.add(new Name(root.name));
|
|
73625
73720
|
|
|
73626
73721
|
if (root.isMesh) {
|
|
73627
73722
|
if (root.isSkinnedMesh) ;
|
|
@@ -79745,6 +79840,22 @@ function m3_cm_compose_transform(
|
|
|
79745
79840
|
*/
|
|
79746
79841
|
const FLT_EPSILON_32 = 1.192092896E-7;
|
|
79747
79842
|
|
|
79843
|
+
/**
|
|
79844
|
+
*
|
|
79845
|
+
* @param {HTMLElement} domElement
|
|
79846
|
+
* @param {boolean} visibility
|
|
79847
|
+
*/
|
|
79848
|
+
function setElementVisibility(domElement, visibility) {
|
|
79849
|
+
const style = domElement.style;
|
|
79850
|
+
|
|
79851
|
+
if (!visibility) {
|
|
79852
|
+
style.display = 'none';
|
|
79853
|
+
} else {
|
|
79854
|
+
// remove display property, this allows style re-flow whereby previous display type is assumed
|
|
79855
|
+
style.removeProperty('display');
|
|
79856
|
+
}
|
|
79857
|
+
}
|
|
79858
|
+
|
|
79748
79859
|
/**
|
|
79749
79860
|
*
|
|
79750
79861
|
* @param {Float32Array} m3
|
|
@@ -79778,21 +79889,6 @@ function writeCssTransformMatrix(m3, domElement) {
|
|
|
79778
79889
|
*/
|
|
79779
79890
|
|
|
79780
79891
|
|
|
79781
|
-
/**
|
|
79782
|
-
*
|
|
79783
|
-
* @param {HTMLElement} domElement
|
|
79784
|
-
* @param {boolean} visibility
|
|
79785
|
-
*/
|
|
79786
|
-
function setElementVisibility(domElement, visibility) {
|
|
79787
|
-
const style = domElement.style;
|
|
79788
|
-
|
|
79789
|
-
if (!visibility) {
|
|
79790
|
-
style.display = 'none';
|
|
79791
|
-
} else {
|
|
79792
|
-
// remove display property, this allows style re-flow whereby previous display type is assumed
|
|
79793
|
-
style.removeProperty('display');
|
|
79794
|
-
}
|
|
79795
|
-
}
|
|
79796
79892
|
|
|
79797
79893
|
/**
|
|
79798
79894
|
*
|
|
@@ -79815,84 +79911,86 @@ const INITIAL_FLAGS = ViewFlags.Visible;
|
|
|
79815
79911
|
* @class
|
|
79816
79912
|
*/
|
|
79817
79913
|
class View {
|
|
79818
|
-
#transform_written = new Float32Array(9);
|
|
79819
|
-
#transform_current = new Float32Array(9);
|
|
79820
79914
|
|
|
79821
79915
|
/**
|
|
79822
|
-
*
|
|
79916
|
+
* Signal bindings, these will be linked and unlinked along with the view
|
|
79917
|
+
* @private
|
|
79918
|
+
* @type {SignalBinding[]}
|
|
79823
79919
|
*/
|
|
79824
|
-
|
|
79825
|
-
/**
|
|
79826
|
-
*
|
|
79827
|
-
* @type {Element|NodeDescription|null}
|
|
79828
|
-
*/
|
|
79829
|
-
this.el = null;
|
|
79920
|
+
bindings = [];
|
|
79830
79921
|
|
|
79831
|
-
|
|
79832
|
-
|
|
79833
|
-
|
|
79834
|
-
|
|
79835
|
-
|
|
79836
|
-
this.bindings = [];
|
|
79922
|
+
/**
|
|
79923
|
+
*
|
|
79924
|
+
* @type {ViewFlags|number}
|
|
79925
|
+
*/
|
|
79926
|
+
flags = INITIAL_FLAGS;
|
|
79837
79927
|
|
|
79838
|
-
|
|
79839
|
-
|
|
79840
|
-
|
|
79841
|
-
|
|
79842
|
-
|
|
79928
|
+
/**
|
|
79929
|
+
* @readonly
|
|
79930
|
+
* @type {Vector2}
|
|
79931
|
+
*/
|
|
79932
|
+
position = new Vector2(0, 0);
|
|
79843
79933
|
|
|
79844
|
-
|
|
79845
|
-
|
|
79846
|
-
|
|
79847
|
-
|
|
79848
|
-
|
|
79934
|
+
/**
|
|
79935
|
+
* @readonly
|
|
79936
|
+
* @type {Vector1}
|
|
79937
|
+
*/
|
|
79938
|
+
rotation = new Vector1(0);
|
|
79849
79939
|
|
|
79850
|
-
|
|
79851
|
-
|
|
79852
|
-
|
|
79853
|
-
|
|
79854
|
-
|
|
79940
|
+
/**
|
|
79941
|
+
* @readonly
|
|
79942
|
+
* @type {Vector2}
|
|
79943
|
+
*/
|
|
79944
|
+
scale = new Vector2(1, 1);
|
|
79855
79945
|
|
|
79856
|
-
|
|
79857
|
-
|
|
79858
|
-
|
|
79859
|
-
|
|
79860
|
-
|
|
79946
|
+
/**
|
|
79947
|
+
* @readonly
|
|
79948
|
+
* @type {Vector2}
|
|
79949
|
+
*/
|
|
79950
|
+
size = new Vector2(0, 0);
|
|
79861
79951
|
|
|
79862
|
-
|
|
79863
|
-
|
|
79864
|
-
|
|
79865
|
-
|
|
79866
|
-
|
|
79952
|
+
/**
|
|
79953
|
+
* Origin from which rotation and scaling is applied
|
|
79954
|
+
* @readonly
|
|
79955
|
+
* @type {Vector2}
|
|
79956
|
+
*/
|
|
79957
|
+
transformOrigin = new Vector2(0.5, 0.5);
|
|
79867
79958
|
|
|
79868
|
-
|
|
79869
|
-
|
|
79870
|
-
|
|
79871
|
-
|
|
79872
|
-
*/
|
|
79873
|
-
this.transformOrigin = new Vector2(0.5, 0.5);
|
|
79959
|
+
on = {
|
|
79960
|
+
linked: new Signal(),
|
|
79961
|
+
unlinked: new Signal()
|
|
79962
|
+
};
|
|
79874
79963
|
|
|
79875
|
-
|
|
79876
|
-
|
|
79877
|
-
|
|
79878
|
-
|
|
79964
|
+
/**
|
|
79965
|
+
*
|
|
79966
|
+
* @type {View[]}
|
|
79967
|
+
*/
|
|
79968
|
+
children = [];
|
|
79879
79969
|
|
|
79880
|
-
|
|
79881
|
-
|
|
79882
|
-
|
|
79883
|
-
|
|
79884
|
-
|
|
79970
|
+
/**
|
|
79971
|
+
*
|
|
79972
|
+
* @type {View|null}
|
|
79973
|
+
*/
|
|
79974
|
+
parent = null;
|
|
79885
79975
|
|
|
79976
|
+
|
|
79977
|
+
#transform_written = new Float32Array(9);
|
|
79978
|
+
#transform_current = new Float32Array(9);
|
|
79979
|
+
|
|
79980
|
+
/**
|
|
79981
|
+
* @constructor
|
|
79982
|
+
*/
|
|
79983
|
+
constructor() {
|
|
79886
79984
|
/**
|
|
79887
79985
|
*
|
|
79888
|
-
* @type {
|
|
79986
|
+
* @type {Element|NodeDescription|null}
|
|
79889
79987
|
*/
|
|
79890
|
-
this.
|
|
79988
|
+
this.el = null;
|
|
79891
79989
|
|
|
79892
|
-
position.onChanged.add(this.__updateTransform, this);
|
|
79893
|
-
scale.onChanged.add(this.__updateTransform, this);
|
|
79894
|
-
rotation.onChanged.add(this.__updateTransform, this);
|
|
79895
|
-
size.onChanged.add(this.__setDimensions, this);
|
|
79990
|
+
this.position.onChanged.add(this.__updateTransform, this);
|
|
79991
|
+
this.scale.onChanged.add(this.__updateTransform, this);
|
|
79992
|
+
this.rotation.onChanged.add(this.__updateTransform, this);
|
|
79993
|
+
this.size.onChanged.add(this.__setDimensions, this);
|
|
79896
79994
|
|
|
79897
79995
|
this.transformOrigin.onChanged.add(this.__setTransformOrigin, this);
|
|
79898
79996
|
}
|