mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 01:03:57 +02:00
Merge pull request #5562 from AlexanderOtavka/overlay-docs
Documentation for overlay changes.
This commit is contained in:
commit
a0e92e71a5
4 changed files with 210 additions and 255 deletions
|
@ -5,6 +5,9 @@
|
|||
// Created by Zander Otavka on 7/24/15
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
// Manage overlays with object oriented goodness, instead of ugly `Overlays.h` methods.
|
||||
// Instead of:
|
||||
//
|
||||
|
@ -22,39 +25,59 @@
|
|||
// ...
|
||||
// billboard.destroy();
|
||||
//
|
||||
// See more on usage below.
|
||||
// More on usage below. Examples in `examples/example/overlayPanelExample.js`.
|
||||
//
|
||||
// Note that including this file will delete Overlays from the global scope. All the
|
||||
// functionality of Overlays is represented here, just better. If you try to use Overlays in
|
||||
// tandem, there may be performance problems or nasty surprises.
|
||||
// Note that including this file will delete `Overlays` from the global scope. All the
|
||||
// functionality of `Overlays` is represented here, just better. If you try to use `Overlays`
|
||||
// in tandem, there may be performance problems or nasty surprises.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
(function() {
|
||||
// Delete `Overlays` from the global scope.
|
||||
var Overlays = this.Overlays;
|
||||
delete this.Overlays;
|
||||
|
||||
|
||||
var ABSTRACT = null;
|
||||
|
||||
var overlays = {};
|
||||
var panels = {};
|
||||
|
||||
var overlayTypes;
|
||||
var overlayTypes = {};
|
||||
|
||||
// Abstract overlay types
|
||||
var Overlay,
|
||||
Overlay2D,
|
||||
Base3DOverlay,
|
||||
Planar3DOverlay,
|
||||
Billboard3DOverlay,
|
||||
Volume3DOverlay;
|
||||
|
||||
// Multiple inheritance mixins
|
||||
var PanelAttachable,
|
||||
Billboardable;
|
||||
function generateOverlayClass(superclass, type, properties) {
|
||||
var that;
|
||||
if (type == ABSTRACT) {
|
||||
that = function(type, params) {
|
||||
superclass.call(this, type, params);
|
||||
};
|
||||
} else {
|
||||
that = function(params) {
|
||||
superclass.call(this, type, params);
|
||||
};
|
||||
overlayTypes[type] = that;
|
||||
}
|
||||
|
||||
that.prototype = new superclass();
|
||||
that.prototype.constructor = that;
|
||||
|
||||
properties.forEach(function(prop) {
|
||||
Object.defineProperty(that.prototype, prop, {
|
||||
get: function() {
|
||||
return Overlays.getProperty(this._id, prop);
|
||||
},
|
||||
set: function(newValue) {
|
||||
var keyValuePair = {};
|
||||
keyValuePair[prop] = newValue;
|
||||
this.setProperties(keyValuePair);
|
||||
},
|
||||
configurable: false
|
||||
});
|
||||
});
|
||||
|
||||
return that;
|
||||
}
|
||||
|
||||
//
|
||||
// Create a new JavaScript object for an overlay of given ID.
|
||||
|
@ -137,249 +160,131 @@
|
|||
}
|
||||
|
||||
|
||||
//
|
||||
// Perform global scoped operations on overlays, such as finding by ray intersection.
|
||||
//
|
||||
OverlayManager = {
|
||||
findOnRay: function(pickRay, knownOverlaysOnly, searchList) {
|
||||
var rayPickResult = Overlays.findRayIntersection(pickRay);
|
||||
if (rayPickResult.intersects) {
|
||||
return findOverlay(rayPickResult.overlayID, knownOverlaysOnly, searchList);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
findAtPoint: function(point, knownOverlaysOnly, searchList) {
|
||||
var foundID = Overlays.getOverlayAtPoint(point);
|
||||
if (foundID) {
|
||||
return findOverlay(foundID, knownOverlaysOnly, searchList);
|
||||
var Overlay = (function() {
|
||||
var that = function(type, params) {
|
||||
if (type && params) {
|
||||
this._id = Overlays.addOverlay(type, params);
|
||||
overlays[this._id] = this;
|
||||
} else {
|
||||
var pickRay = Camera.computePickRay(point.x, point.y);
|
||||
return OverlayManager.findOnRay(pickRay, knownOverlaysOnly, searchList);
|
||||
this._id = 0;
|
||||
}
|
||||
},
|
||||
makeSearchList: function(array) {
|
||||
var searchList = {};
|
||||
array.forEach(function(object) {
|
||||
searchList[object._id] = object;
|
||||
});
|
||||
return searchList;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
that.prototype.constructor = that;
|
||||
|
||||
//
|
||||
// Object oriented abstraction layer for overlays.
|
||||
//
|
||||
// Usage:
|
||||
// // Create an overlay
|
||||
// var billboard = new Image3DOverlay({
|
||||
// visible: true,
|
||||
// isFacingAvatar: true,
|
||||
// ignoreRayIntersections: false
|
||||
// });
|
||||
//
|
||||
// // Get a property
|
||||
// var isVisible = billboard.visible;
|
||||
//
|
||||
// // Set a single property
|
||||
// billboard.position = { x: 1, y: 3, z: 2 };
|
||||
//
|
||||
// // Set multiple properties at the same time
|
||||
// billboard.setProperties({
|
||||
// url: "http://images.com/overlayImage.jpg",
|
||||
// dimensions: { x: 2, y: 2 }
|
||||
// });
|
||||
//
|
||||
// // Clone an overlay
|
||||
// var clonedBillboard = billboard.clone();
|
||||
//
|
||||
// // Remove an overlay from the world
|
||||
// billboard.destroy();
|
||||
//
|
||||
// // Remember, there is a poor orphaned JavaScript object left behind. You should
|
||||
// // remove any references to it so you don't accidentally try to modify an overlay that
|
||||
// // isn't there.
|
||||
// billboard = undefined;
|
||||
//
|
||||
(function() {
|
||||
var ABSTRACT = null;
|
||||
overlayTypes = {};
|
||||
|
||||
function generateOverlayClass(superclass, type, properties) {
|
||||
var that;
|
||||
if (type == ABSTRACT) {
|
||||
that = function(type, params) {
|
||||
superclass.call(this, type, params);
|
||||
};
|
||||
} else {
|
||||
that = function(params) {
|
||||
superclass.call(this, type, params);
|
||||
};
|
||||
overlayTypes[type] = that;
|
||||
Object.defineProperty(that.prototype, "isLoaded", {
|
||||
get: function() {
|
||||
return Overlays.isLoaded(this._id);
|
||||
}
|
||||
});
|
||||
|
||||
that.prototype = new superclass();
|
||||
that.prototype.constructor = that;
|
||||
Object.defineProperty(that.prototype, "parentPanel", {
|
||||
get: function() {
|
||||
return findPanel(Overlays.getParentPanel(this._id));
|
||||
}
|
||||
});
|
||||
|
||||
properties.forEach(function(prop) {
|
||||
Object.defineProperty(that.prototype, prop, {
|
||||
get: function() {
|
||||
return Overlays.getProperty(this._id, prop);
|
||||
},
|
||||
set: function(newValue) {
|
||||
var keyValuePair = {};
|
||||
keyValuePair[prop] = newValue;
|
||||
this.setProperties(keyValuePair);
|
||||
},
|
||||
configurable: false
|
||||
});
|
||||
});
|
||||
that.prototype.getTextSize = function(text) {
|
||||
return Overlays.textSize(this._id, text);
|
||||
};
|
||||
|
||||
return that;
|
||||
}
|
||||
that.prototype.setProperties = function(properties) {
|
||||
Overlays.editOverlay(this._id, properties);
|
||||
};
|
||||
|
||||
Overlay = (function() {
|
||||
var that = function(type, params) {
|
||||
if (type && params) {
|
||||
this._id = Overlays.addOverlay(type, params);
|
||||
overlays[this._id] = this;
|
||||
} else {
|
||||
this._id = 0;
|
||||
}
|
||||
};
|
||||
that.prototype.clone = function() {
|
||||
return makeOverlayFromId(Overlays.cloneOverlay(this._id));
|
||||
};
|
||||
|
||||
that.prototype.constructor = that;
|
||||
that.prototype.destroy = function() {
|
||||
Overlays.deleteOverlay(this._id);
|
||||
};
|
||||
|
||||
Object.defineProperty(that.prototype, "isLoaded", {
|
||||
get: function() {
|
||||
return Overlays.isLoaded(this._id);
|
||||
}
|
||||
});
|
||||
that.prototype.isPanelAttachable = function() {
|
||||
return false;
|
||||
};
|
||||
|
||||
Object.defineProperty(that.prototype, "parentPanel", {
|
||||
get: function() {
|
||||
return findPanel(Overlays.getParentPanel(this._id));
|
||||
}
|
||||
});
|
||||
|
||||
that.prototype.getTextSize = function(text) {
|
||||
return Overlays.textSize(this._id, text);
|
||||
};
|
||||
|
||||
that.prototype.setProperties = function(properties) {
|
||||
Overlays.editOverlay(this._id, properties);
|
||||
};
|
||||
|
||||
that.prototype.clone = function() {
|
||||
return makeOverlayFromId(Overlays.cloneOverlay(this._id));
|
||||
};
|
||||
|
||||
that.prototype.destroy = function() {
|
||||
Overlays.deleteOverlay(this._id);
|
||||
};
|
||||
|
||||
that.prototype.isPanelAttachable = function() {
|
||||
return false;
|
||||
};
|
||||
|
||||
return generateOverlayClass(that, ABSTRACT, [
|
||||
"alpha", "glowLevel", "pulseMax", "pulseMin", "pulsePeriod", "glowLevelPulse",
|
||||
"alphaPulse", "colorPulse", "visible", "anchor"
|
||||
]);
|
||||
})();
|
||||
|
||||
// Supports multiple inheritance of properties. Just `concat` them onto the end of the
|
||||
// properties list.
|
||||
PanelAttachable = ["offsetPosition", "offsetRotation", "offsetScale"];
|
||||
Billboardable = ["isFacingAvatar"];
|
||||
|
||||
Overlay2D = generateOverlayClass(Overlay, ABSTRACT, [
|
||||
"bounds", "x", "y", "width", "height"
|
||||
]);
|
||||
|
||||
Base3DOverlay = generateOverlayClass(Overlay, ABSTRACT, [
|
||||
"position", "lineWidth", "rotation", "isSolid", "isFilled", "isWire", "isDashedLine",
|
||||
"ignoreRayIntersection", "drawInFront", "drawOnHUD"
|
||||
]);
|
||||
|
||||
Planar3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [
|
||||
"dimensions"
|
||||
]);
|
||||
|
||||
Billboard3DOverlay = generateOverlayClass(Planar3DOverlay, ABSTRACT, [
|
||||
].concat(PanelAttachable).concat(Billboardable));
|
||||
Billboard3DOverlay.prototype.isPanelAttachable = function() { return true; };
|
||||
|
||||
Volume3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [
|
||||
"dimensions"
|
||||
]);
|
||||
|
||||
generateOverlayClass(Overlay2D, "image", [
|
||||
"subImage", "imageURL"
|
||||
]);
|
||||
|
||||
generateOverlayClass(Billboard3DOverlay, "image3d", [
|
||||
"url", "subImage"
|
||||
]);
|
||||
|
||||
generateOverlayClass(Overlay2D, "text", [
|
||||
"font", "text", "backgroundColor", "backgroundAlpha", "leftMargin", "topMargin"
|
||||
]);
|
||||
|
||||
generateOverlayClass(Billboard3DOverlay, "text3d", [
|
||||
"text", "backgroundColor", "backgroundAlpha", "lineHeight", "leftMargin", "topMargin",
|
||||
"rightMargin", "bottomMargin"
|
||||
]);
|
||||
|
||||
generateOverlayClass(Volume3DOverlay, "cube", [
|
||||
"borderSize"
|
||||
]);
|
||||
|
||||
generateOverlayClass(Volume3DOverlay, "sphere", [
|
||||
]);
|
||||
|
||||
generateOverlayClass(Planar3DOverlay, "circle3d", [
|
||||
"startAt", "endAt", "outerRadius", "innerRadius", "hasTickMarks",
|
||||
"majorTickMarksAngle", "minorTickMarksAngle", "majorTickMarksLength",
|
||||
"minorTickMarksLength", "majorTickMarksColor", "minorTickMarksColor"
|
||||
]);
|
||||
|
||||
generateOverlayClass(Planar3DOverlay, "rectangle3d", [
|
||||
]);
|
||||
|
||||
generateOverlayClass(Base3DOverlay, "line3d", [
|
||||
"start", "end"
|
||||
]);
|
||||
|
||||
generateOverlayClass(Planar3DOverlay, "grid", [
|
||||
"minorGridWidth", "majorGridEvery"
|
||||
]);
|
||||
|
||||
generateOverlayClass(Volume3DOverlay, "localmodels", [
|
||||
]);
|
||||
|
||||
generateOverlayClass(Volume3DOverlay, "model", [
|
||||
"url", "dimensions", "textures"
|
||||
return generateOverlayClass(that, ABSTRACT, [
|
||||
"alpha", "glowLevel", "pulseMax", "pulseMin", "pulsePeriod", "glowLevelPulse",
|
||||
"alphaPulse", "colorPulse", "visible", "anchor"
|
||||
]);
|
||||
})();
|
||||
|
||||
ImageOverlay = overlayTypes["image"];
|
||||
Image3DOverlay = overlayTypes["image3d"];
|
||||
TextOverlay = overlayTypes["text"];
|
||||
Text3DOverlay = overlayTypes["text3d"];
|
||||
Cube3DOverlay = overlayTypes["cube"];
|
||||
Sphere3DOverlay = overlayTypes["sphere"];
|
||||
Circle3DOverlay = overlayTypes["circle3d"];
|
||||
Rectangle3DOverlay = overlayTypes["rectangle3d"];
|
||||
Line3DOverlay = overlayTypes["line3d"];
|
||||
Grid3DOverlay = overlayTypes["grid"];
|
||||
LocalModelsOverlay = overlayTypes["localmodels"];
|
||||
ModelOverlay = overlayTypes["model"];
|
||||
// Supports multiple inheritance of properties. Just `concat` them onto the end of the
|
||||
// properties list.
|
||||
var PanelAttachable = ["offsetPosition", "offsetRotation", "offsetScale"];
|
||||
var Billboardable = ["isFacingAvatar"];
|
||||
|
||||
var Overlay2D = generateOverlayClass(Overlay, ABSTRACT, [
|
||||
"bounds", "x", "y", "width", "height"
|
||||
]);
|
||||
|
||||
var Base3DOverlay = generateOverlayClass(Overlay, ABSTRACT, [
|
||||
"position", "lineWidth", "rotation", "isSolid", "isFilled", "isWire", "isDashedLine",
|
||||
"ignoreRayIntersection", "drawInFront", "drawOnHUD"
|
||||
]);
|
||||
|
||||
var Planar3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [
|
||||
"dimensions"
|
||||
]);
|
||||
|
||||
var Billboard3DOverlay = generateOverlayClass(Planar3DOverlay, ABSTRACT, [
|
||||
].concat(PanelAttachable).concat(Billboardable));
|
||||
Billboard3DOverlay.prototype.isPanelAttachable = function() { return true; };
|
||||
|
||||
var Volume3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [
|
||||
"dimensions"
|
||||
]);
|
||||
|
||||
ImageOverlay = generateOverlayClass(Overlay2D, "image", [
|
||||
"subImage", "imageURL"
|
||||
]);
|
||||
|
||||
Image3DOverlay = generateOverlayClass(Billboard3DOverlay, "image3d", [
|
||||
"url", "subImage"
|
||||
]);
|
||||
|
||||
TextOverlay = generateOverlayClass(Overlay2D, "text", [
|
||||
"font", "text", "backgroundColor", "backgroundAlpha", "leftMargin", "topMargin"
|
||||
]);
|
||||
|
||||
Text3DOverlay = generateOverlayClass(Billboard3DOverlay, "text3d", [
|
||||
"text", "backgroundColor", "backgroundAlpha", "lineHeight", "leftMargin", "topMargin",
|
||||
"rightMargin", "bottomMargin"
|
||||
]);
|
||||
|
||||
Cube3DOverlay = generateOverlayClass(Volume3DOverlay, "cube", [
|
||||
"borderSize"
|
||||
]);
|
||||
|
||||
Sphere3DOverlay = generateOverlayClass(Volume3DOverlay, "sphere", [
|
||||
]);
|
||||
|
||||
Circle3DOverlay = generateOverlayClass(Planar3DOverlay, "circle3d", [
|
||||
"startAt", "endAt", "outerRadius", "innerRadius", "hasTickMarks",
|
||||
"majorTickMarksAngle", "minorTickMarksAngle", "majorTickMarksLength",
|
||||
"minorTickMarksLength", "majorTickMarksColor", "minorTickMarksColor"
|
||||
]);
|
||||
|
||||
Rectangle3DOverlay = generateOverlayClass(Planar3DOverlay, "rectangle3d", [
|
||||
]);
|
||||
|
||||
Line3DOverlay = generateOverlayClass(Base3DOverlay, "line3d", [
|
||||
"start", "end"
|
||||
]);
|
||||
|
||||
Grid3DOverlay = generateOverlayClass(Planar3DOverlay, "grid", [
|
||||
"minorGridWidth", "majorGridEvery"
|
||||
]);
|
||||
|
||||
LocalModelsOverlay = generateOverlayClass(Volume3DOverlay, "localmodels", [
|
||||
]);
|
||||
|
||||
ModelOverlay = generateOverlayClass(Volume3DOverlay, "model", [
|
||||
"url", "dimensions", "textures"
|
||||
]);
|
||||
|
||||
|
||||
//
|
||||
// Object oriented abstraction layer for panels.
|
||||
//
|
||||
OverlayPanel = (function() {
|
||||
var that = function(params) {
|
||||
this._id = Overlays.addPanel(params);
|
||||
|
@ -455,6 +360,35 @@
|
|||
})();
|
||||
|
||||
|
||||
OverlayManager = {
|
||||
findOnRay: function(pickRay, knownOverlaysOnly, searchList) {
|
||||
var rayPickResult = Overlays.findRayIntersection(pickRay);
|
||||
if (rayPickResult.intersects) {
|
||||
return findOverlay(rayPickResult.overlayID, knownOverlaysOnly, searchList);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
findAtPoint: function(point, knownOverlaysOnly, searchList) {
|
||||
var foundID = Overlays.getOverlayAtPoint(point);
|
||||
if (foundID) {
|
||||
return findOverlay(foundID, knownOverlaysOnly, searchList);
|
||||
} else {
|
||||
var pickRay = Camera.computePickRay(point.x, point.y);
|
||||
return OverlayManager.findOnRay(pickRay, knownOverlaysOnly, searchList);
|
||||
}
|
||||
},
|
||||
makeSearchList: function(array) {
|
||||
var searchList = {};
|
||||
array.forEach(function(object) {
|
||||
searchList[object._id] = object;
|
||||
});
|
||||
return searchList;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Threadsafe cleanup of JavaScript objects.
|
||||
|
||||
function onOverlayDeleted(id) {
|
||||
if (id in overlays) {
|
||||
if (overlays[id].parentPanel) {
|
||||
|
|
|
@ -5,14 +5,15 @@
|
|||
// Modified by Zander Otavka on 7/15/15
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Exposes methods for managing `Overlay`s and `OverlayPanel`s to scripts.
|
||||
//
|
||||
// YOU SHOULD NOT USE `Overlays` DIRECTLY, unless you like pain and deprecation. Instead, use the
|
||||
// object oriented abstraction layer found in `examples/libraries/overlayUtils.js`.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
// Exposes methods to scripts for managing `Overlay`s and `OverlayPanel`s.
|
||||
//
|
||||
// YOU SHOULD NOT USE `Overlays` DIRECTLY, unless you like pain and deprecation. Instead, use
|
||||
// the object oriented API replacement found in `examples/libraries/overlayManager.js`. See
|
||||
// that file for docs and usage.
|
||||
//
|
||||
|
||||
#ifndef hifi_Overlays_h
|
||||
#define hifi_Overlays_h
|
||||
|
@ -57,11 +58,11 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, R
|
|||
|
||||
class Overlays : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
|
||||
public:
|
||||
Overlays();
|
||||
~Overlays();
|
||||
|
||||
|
||||
void init();
|
||||
void update(float deltatime);
|
||||
void renderHUD(RenderArgs* renderArgs);
|
||||
|
@ -103,7 +104,7 @@ public slots:
|
|||
|
||||
/// returns details about the closest 3D Overlay hit by the pick ray
|
||||
RayToOverlayIntersectionResult findRayIntersection(const PickRay& ray);
|
||||
|
||||
|
||||
/// returns whether the overlay's assets are loaded or not
|
||||
bool isLoaded(unsigned int id);
|
||||
|
||||
|
@ -153,5 +154,5 @@ private:
|
|||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // hifi_Overlays_h
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// PanelAttachable.cpp
|
||||
// hifi
|
||||
// interface/src/ui/overlays
|
||||
//
|
||||
// Created by Zander Otavka on 7/15/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
|
|
|
@ -8,6 +8,24 @@
|
|||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
// Base class for anything that can attach itself to an `OverlayPanel` as a child.
|
||||
// `PanelAttachable keeps an `std::shared_ptr` to it's parent panel, and sets its
|
||||
// transformations and visibility based on the parent.
|
||||
//
|
||||
// When subclassing `PanelAttachable`, make sure `applyTransformTo`, `getProperty`, and
|
||||
// `setProperties are all called in the appropriate places. Look through `Image3DOverlay` and
|
||||
// `Billboard3DOverlay` for examples. Pay special attention to `applyTransformTo`; it should
|
||||
// be called in three places for `Overlay`s: `render`, `update`, and `findRayIntersection`.
|
||||
//
|
||||
// When overriding `applyTransformTo`, make sure to wrap all of your code, including the call
|
||||
// to the superclass method, with the following `if` block. Then call the superclass method
|
||||
// with force = true.
|
||||
//
|
||||
// if (force || usecTimestampNow() > _transformExpiry) {
|
||||
// PanelAttachable::applyTransformTo(transform, true);
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
|
||||
#ifndef hifi_PanelAttachable_h
|
||||
#define hifi_PanelAttachable_h
|
||||
|
@ -42,6 +60,8 @@ protected:
|
|||
QScriptValue getProperty(QScriptEngine* scriptEngine, const QString& property);
|
||||
void setProperties(const QScriptValue& properties);
|
||||
|
||||
/// set position, rotation and scale on transform based on offsets, and parent panel offsets
|
||||
/// if force is false, only apply transform if it hasn't been applied in the last .1 seconds
|
||||
virtual void applyTransformTo(Transform& transform, bool force = false);
|
||||
quint64 _transformExpiry = 0;
|
||||
|
||||
|
|
Loading…
Reference in a new issue