|
@@ -18,43 +18,51 @@ var BABYLON;
|
|
*/
|
|
*/
|
|
function Group2D() {
|
|
function Group2D() {
|
|
_super.call(this);
|
|
_super.call(this);
|
|
- this._primDirtyList = new Array();
|
|
|
|
- this._childrenRenderableGroups = new Array();
|
|
|
|
- this._renderGroupInstancesInfo = new BABYLON.StringDictionary();
|
|
|
|
}
|
|
}
|
|
/**
|
|
/**
|
|
* Create an Logical or Renderable Group.
|
|
* Create an Logical or Renderable Group.
|
|
* @param parent the parent primitive, must be a valid primitive (or the Canvas)
|
|
* @param parent the parent primitive, must be a valid primitive (or the Canvas)
|
|
* options:
|
|
* options:
|
|
* - id a text identifier, for information purpose
|
|
* - id a text identifier, for information purpose
|
|
- * - position: the X & Y positions relative to its parent, default is [0;0]
|
|
|
|
|
|
+ * - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
|
|
* - origin: define the normalized origin point location, default [0.5;0.5]
|
|
* - origin: define the normalized origin point location, default [0.5;0.5]
|
|
- * - size: the size of the group, if null the size will be computed from its content, default is null.
|
|
|
|
|
|
+ * - size: the size of the group. Alternatively the width and height properties can be set. If null the size will be computed from its content, default is null.
|
|
* - cacheBehavior: Define how the group should behave regarding the Canvas's cache strategy, default is Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY
|
|
* - cacheBehavior: Define how the group should behave regarding the Canvas's cache strategy, default is Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY
|
|
|
|
+ * - isVisible: true if the group 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.
|
|
*/
|
|
*/
|
|
Group2D.CreateGroup2D = function (parent, options) {
|
|
Group2D.CreateGroup2D = function (parent, options) {
|
|
BABYLON.Prim2DBase.CheckParent(parent);
|
|
BABYLON.Prim2DBase.CheckParent(parent);
|
|
var g = new Group2D();
|
|
var g = new Group2D();
|
|
- g.setupGroup2D(parent.owner, parent, options && options.id || null, options && options.position || BABYLON.Vector2.Zero(), options && options.origin || null, options && options.size || null, options && options.cacheBehavior || Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY);
|
|
|
|
|
|
+ if (!options) {
|
|
|
|
+ g.setupGroup2D(parent.owner, parent, null, BABYLON.Vector2.Zero(), null, null, true, Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY, null, null, null, null, null, null);
|
|
|
|
+ }
|
|
|
|
+ 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)));
|
|
|
|
+ g.setupGroup2D(parent.owner, parent, options.id || null, pos, options.origin || null, size, options.isVisible || true, options.cacheBehavior || Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY, options.marginTop, options.marginLeft, options.marginRight, options.marginBottom, options.hAlignment || BABYLON.Prim2DBase.HAlignLeft, options.vAlignment || BABYLON.Prim2DBase.VAlignTop);
|
|
|
|
+ }
|
|
return g;
|
|
return g;
|
|
};
|
|
};
|
|
Group2D._createCachedCanvasGroup = function (owner) {
|
|
Group2D._createCachedCanvasGroup = function (owner) {
|
|
var g = new Group2D();
|
|
var g = new Group2D();
|
|
- g.setupGroup2D(owner, null, "__cachedCanvasGroup__", BABYLON.Vector2.Zero(), null);
|
|
|
|
|
|
+ g.setupGroup2D(owner, null, "__cachedCanvasGroup__", BABYLON.Vector2.Zero(), BABYLON.Vector2.Zero(), null, true, Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY, null, null, null, null, null, null);
|
|
g.origin = BABYLON.Vector2.Zero();
|
|
g.origin = BABYLON.Vector2.Zero();
|
|
return g;
|
|
return g;
|
|
};
|
|
};
|
|
Group2D.prototype.applyCachedTexture = function (vertexData, material) {
|
|
Group2D.prototype.applyCachedTexture = function (vertexData, material) {
|
|
this._bindCacheTarget();
|
|
this._bindCacheTarget();
|
|
var uv = vertexData.uvs;
|
|
var uv = vertexData.uvs;
|
|
- var nodeuv = this._cacheNode.UVs;
|
|
|
|
|
|
+ var nodeuv = this._renderableData._cacheNode.UVs;
|
|
for (var i = 0; i < 4; i++) {
|
|
for (var i = 0; i < 4; i++) {
|
|
uv[i * 2 + 0] = nodeuv[i].x;
|
|
uv[i * 2 + 0] = nodeuv[i].x;
|
|
uv[i * 2 + 1] = nodeuv[i].y;
|
|
uv[i * 2 + 1] = nodeuv[i].y;
|
|
}
|
|
}
|
|
- material.diffuseTexture = this._cacheTexture;
|
|
|
|
|
|
+ material.diffuseTexture = this._renderableData._cacheTexture;
|
|
material.emissiveColor = new BABYLON.Color3(1, 1, 1);
|
|
material.emissiveColor = new BABYLON.Color3(1, 1, 1);
|
|
- this._cacheTexture.hasAlpha = true;
|
|
|
|
|
|
+ this._renderableData._cacheTexture.hasAlpha = true;
|
|
this._unbindCacheTarget();
|
|
this._unbindCacheTarget();
|
|
};
|
|
};
|
|
/**
|
|
/**
|
|
@@ -64,31 +72,15 @@ var BABYLON;
|
|
if (!_super.prototype.dispose.call(this)) {
|
|
if (!_super.prototype.dispose.call(this)) {
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
- if (this._cacheRenderSprite) {
|
|
|
|
- this._cacheRenderSprite.dispose();
|
|
|
|
- this._cacheRenderSprite = null;
|
|
|
|
- }
|
|
|
|
- if (this._cacheTexture && this._cacheNode) {
|
|
|
|
- this._cacheTexture.freeRect(this._cacheNode);
|
|
|
|
- this._cacheTexture = null;
|
|
|
|
- this._cacheNode = null;
|
|
|
|
- }
|
|
|
|
- if (this._primDirtyList) {
|
|
|
|
- this._primDirtyList.splice(0);
|
|
|
|
- this._primDirtyList = null;
|
|
|
|
- }
|
|
|
|
- if (this._renderGroupInstancesInfo) {
|
|
|
|
- this._renderGroupInstancesInfo.forEach(function (k, v) {
|
|
|
|
- v.dispose();
|
|
|
|
- });
|
|
|
|
- this._renderGroupInstancesInfo = null;
|
|
|
|
|
|
+ if (this._renderableData) {
|
|
|
|
+ this._renderableData.dispose();
|
|
|
|
+ this._renderableData = null;
|
|
}
|
|
}
|
|
return true;
|
|
return true;
|
|
};
|
|
};
|
|
- Group2D.prototype.setupGroup2D = function (owner, parent, id, position, origin, size, cacheBehavior) {
|
|
|
|
- if (cacheBehavior === void 0) { cacheBehavior = Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY; }
|
|
|
|
|
|
+ Group2D.prototype.setupGroup2D = function (owner, parent, id, position, origin, size, isVisible, cacheBehavior, marginTop, marginLeft, marginRight, marginBottom, hAlign, vAlign) {
|
|
this._cacheBehavior = cacheBehavior;
|
|
this._cacheBehavior = cacheBehavior;
|
|
- this.setupPrim2DBase(owner, parent, id, position, origin);
|
|
|
|
|
|
+ this.setupPrim2DBase(owner, parent, id, position, origin, isVisible, marginTop, marginLeft, marginRight, marginBottom, hAlign, vAlign);
|
|
this.size = size;
|
|
this.size = size;
|
|
this._viewportPosition = BABYLON.Vector2.Zero();
|
|
this._viewportPosition = BABYLON.Vector2.Zero();
|
|
};
|
|
};
|
|
@@ -168,12 +160,13 @@ var BABYLON;
|
|
configurable: true
|
|
configurable: true
|
|
});
|
|
});
|
|
Group2D.prototype._addPrimToDirtyList = function (prim) {
|
|
Group2D.prototype._addPrimToDirtyList = function (prim) {
|
|
- this._primDirtyList.push(prim);
|
|
|
|
|
|
+ this._renderableData._primDirtyList.push(prim);
|
|
};
|
|
};
|
|
- Group2D.prototype._renderCachedCanvas = function (context) {
|
|
|
|
|
|
+ Group2D.prototype._renderCachedCanvas = function () {
|
|
this.updateGlobalTransVis(true);
|
|
this.updateGlobalTransVis(true);
|
|
|
|
+ var context = new BABYLON.PreapreRender2DContext();
|
|
this._prepareGroupRender(context);
|
|
this._prepareGroupRender(context);
|
|
- this._groupRender(context);
|
|
|
|
|
|
+ this._groupRender();
|
|
};
|
|
};
|
|
Group2D.prototype.levelIntersect = function (intersectInfo) {
|
|
Group2D.prototype.levelIntersect = function (intersectInfo) {
|
|
// If we've made it so far it means the boundingInfo intersection test succeed, the Group2D is shaped the same, so we always return true
|
|
// If we've made it so far it means the boundingInfo intersection test succeed, the Group2D is shaped the same, so we always return true
|
|
@@ -194,8 +187,8 @@ var BABYLON;
|
|
Group2D.prototype._prepareGroupRender = function (context) {
|
|
Group2D.prototype._prepareGroupRender = function (context) {
|
|
var sortedDirtyList = null;
|
|
var sortedDirtyList = null;
|
|
// Update the Global Transformation and visibility status of the changed primitives
|
|
// Update the Global Transformation and visibility status of the changed primitives
|
|
- if ((this._primDirtyList.length > 0) || context.forceRefreshPrimitive) {
|
|
|
|
- sortedDirtyList = this._primDirtyList.sort(function (a, b) { return a.hierarchyDepth - b.hierarchyDepth; });
|
|
|
|
|
|
+ if ((this._renderableData._primDirtyList.length > 0) || context.forceRefreshPrimitive) {
|
|
|
|
+ sortedDirtyList = this._renderableData._primDirtyList.sort(function (a, b) { return a.hierarchyDepth - b.hierarchyDepth; });
|
|
this.updateGlobalTransVisOf(sortedDirtyList, true);
|
|
this.updateGlobalTransVisOf(sortedDirtyList, true);
|
|
}
|
|
}
|
|
// Setup the size of the rendering viewport
|
|
// Setup the size of the rendering viewport
|
|
@@ -232,7 +225,7 @@ var BABYLON;
|
|
}
|
|
}
|
|
this._viewportSize = newSize;
|
|
this._viewportSize = newSize;
|
|
}
|
|
}
|
|
- if ((this._primDirtyList.length > 0) || context.forceRefreshPrimitive) {
|
|
|
|
|
|
+ if ((this._renderableData._primDirtyList.length > 0) || context.forceRefreshPrimitive) {
|
|
// If the group is cached, set the dirty flag to true because of the incoming changes
|
|
// If the group is cached, set the dirty flag to true because of the incoming changes
|
|
this._cacheGroupDirty = this._isCachedGroup;
|
|
this._cacheGroupDirty = this._isCachedGroup;
|
|
// If it's a force refresh, prepare all the children
|
|
// If it's a force refresh, prepare all the children
|
|
@@ -246,7 +239,7 @@ var BABYLON;
|
|
// Each primitive that changed at least once was added into the primDirtyList, we have to sort this level using
|
|
// Each primitive that changed at least once was added into the primDirtyList, we have to sort this level using
|
|
// the hierarchyDepth in order to prepare primitives from top to bottom
|
|
// the hierarchyDepth in order to prepare primitives from top to bottom
|
|
if (!sortedDirtyList) {
|
|
if (!sortedDirtyList) {
|
|
- sortedDirtyList = this._primDirtyList.sort(function (a, b) { return a.hierarchyDepth - b.hierarchyDepth; });
|
|
|
|
|
|
+ sortedDirtyList = this._renderableData._primDirtyList.sort(function (a, b) { return a.hierarchyDepth - b.hierarchyDepth; });
|
|
}
|
|
}
|
|
sortedDirtyList.forEach(function (p) {
|
|
sortedDirtyList.forEach(function (p) {
|
|
// We need to check if prepare is needed because even if the primitive is in the dirtyList, its parent primitive may also have been modified, then prepared, then recurse on its children primitives (this one for instance) if the changes where impacting them.
|
|
// We need to check if prepare is needed because even if the primitive is in the dirtyList, its parent primitive may also have been modified, then prepared, then recurse on its children primitives (this one for instance) if the changes where impacting them.
|
|
@@ -256,23 +249,23 @@ var BABYLON;
|
|
}
|
|
}
|
|
});
|
|
});
|
|
// Everything is updated, clear the dirty list
|
|
// Everything is updated, clear the dirty list
|
|
- this._primDirtyList.forEach(function (p) { return p._resetPropertiesDirty(); });
|
|
|
|
- this._primDirtyList.splice(0);
|
|
|
|
|
|
+ this._renderableData._primDirtyList.forEach(function (p) { return p._resetPropertiesDirty(); });
|
|
|
|
+ this._renderableData._primDirtyList.splice(0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// A renderable group has a list of direct children that are also renderable groups, we recurse on them to also prepare them
|
|
// A renderable group has a list of direct children that are also renderable groups, we recurse on them to also prepare them
|
|
- this._childrenRenderableGroups.forEach(function (g) {
|
|
|
|
|
|
+ this._renderableData._childrenRenderableGroups.forEach(function (g) {
|
|
g._prepareGroupRender(context);
|
|
g._prepareGroupRender(context);
|
|
});
|
|
});
|
|
};
|
|
};
|
|
- Group2D.prototype._groupRender = function (context) {
|
|
|
|
|
|
+ Group2D.prototype._groupRender = function () {
|
|
var _this = this;
|
|
var _this = this;
|
|
var engine = this.owner.engine;
|
|
var engine = this.owner.engine;
|
|
var failedCount = 0;
|
|
var failedCount = 0;
|
|
// First recurse to children render group to render them (in their cache or on screen)
|
|
// First recurse to children render group to render them (in their cache or on screen)
|
|
- for (var _i = 0, _a = this._childrenRenderableGroups; _i < _a.length; _i++) {
|
|
|
|
|
|
+ for (var _i = 0, _a = this._renderableData._childrenRenderableGroups; _i < _a.length; _i++) {
|
|
var childGroup = _a[_i];
|
|
var childGroup = _a[_i];
|
|
- childGroup._groupRender(context);
|
|
|
|
|
|
+ childGroup._groupRender();
|
|
}
|
|
}
|
|
// Render the primitives if needed: either if we don't cache the content or if the content is cached but has changed
|
|
// Render the primitives if needed: either if we don't cache the content or if the content is cached but has changed
|
|
if (!this.isCachedGroup || this._cacheGroupDirty) {
|
|
if (!this.isCachedGroup || this._cacheGroupDirty) {
|
|
@@ -282,47 +275,65 @@ var BABYLON;
|
|
else {
|
|
else {
|
|
var curVP = engine.setDirectViewport(this._viewportPosition.x, this._viewportPosition.y, this._viewportSize.width, this._viewportSize.height);
|
|
var curVP = engine.setDirectViewport(this._viewportPosition.x, this._viewportPosition.y, this._viewportSize.width, this._viewportSize.height);
|
|
}
|
|
}
|
|
|
|
+ // ===================================================================
|
|
|
|
+ // First pass, update the InstancedArray and render Opaque primitives
|
|
|
|
+ // Disable Alpha Testing, Enable Depth Write
|
|
|
|
+ engine.setAlphaTesting(false);
|
|
|
|
+ engine.setDepthWrite(true);
|
|
// For each different model of primitive to render
|
|
// For each different model of primitive to render
|
|
- var totalRenderCount_1 = 0;
|
|
|
|
- this._renderGroupInstancesInfo.forEach(function (k, v) {
|
|
|
|
- // This part will pack the dynamicfloatarray and update the instanced array WebGLBufffer
|
|
|
|
- // Skip it if instanced arrays are not supported
|
|
|
|
- if (_this.owner.supportInstancedArray) {
|
|
|
|
- for (var i = 0; i < v._instancesPartsData.length; i++) {
|
|
|
|
- // If the instances of the model was changed, pack the data
|
|
|
|
- var array = v._instancesPartsData[i];
|
|
|
|
- var instanceData_1 = array.pack();
|
|
|
|
- totalRenderCount_1 += array.usedElementCount;
|
|
|
|
- // Compute the size the instance buffer should have
|
|
|
|
- var neededSize = array.usedElementCount * array.stride * 4;
|
|
|
|
- // Check if we have to (re)create the instancesBuffer because there's none or the size is too small
|
|
|
|
- if (!v._instancesPartsBuffer[i] || (v._instancesPartsBufferSize[i] < neededSize)) {
|
|
|
|
- if (v._instancesPartsBuffer[i]) {
|
|
|
|
- engine.deleteInstancesBuffer(v._instancesPartsBuffer[i]);
|
|
|
|
- }
|
|
|
|
- v._instancesPartsBuffer[i] = engine.createInstancesBuffer(neededSize);
|
|
|
|
- v._instancesPartsBufferSize[i] = neededSize;
|
|
|
|
- v._dirtyInstancesData = false;
|
|
|
|
- // Update the WebGL buffer to match the new content of the instances data
|
|
|
|
- engine._gl.bufferSubData(engine._gl.ARRAY_BUFFER, 0, instanceData_1);
|
|
|
|
- }
|
|
|
|
- else if (v._dirtyInstancesData) {
|
|
|
|
- // Update the WebGL buffer to match the new content of the instances data
|
|
|
|
- engine._gl.bindBuffer(engine._gl.ARRAY_BUFFER, v._instancesPartsBuffer[i]);
|
|
|
|
- engine._gl.bufferSubData(engine._gl.ARRAY_BUFFER, 0, instanceData_1);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- v._dirtyInstancesData = false;
|
|
|
|
|
|
+ var context = new BABYLON.Render2DContext(BABYLON.Render2DContext.RenderModeOpaque);
|
|
|
|
+ this._renderableData._renderGroupInstancesInfo.forEach(function (k, v) {
|
|
|
|
+ // Prepare the context object, update the WebGL Instanced Array buffer if needed
|
|
|
|
+ var renderCount = _this._prepareContext(engine, context, v);
|
|
|
|
+ // If null is returned, there's no opaque data to render
|
|
|
|
+ if (renderCount === null) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ // Submit render only if we have something to render (everything may be hidden and the floatarray empty)
|
|
|
|
+ if (!_this.owner.supportInstancedArray || renderCount > 0) {
|
|
|
|
+ // render all the instances of this model, if the render method returns true then our instances are no longer dirty
|
|
|
|
+ var renderFailed = !v.modelRenderCache.render(v, context);
|
|
|
|
+ // Update dirty flag/related
|
|
|
|
+ v.opaqueDirty = renderFailed;
|
|
|
|
+ failedCount += renderFailed ? 1 : 0;
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ // =======================================================================
|
|
|
|
+ // Second pass, update the InstancedArray and render AlphaTest primitives
|
|
|
|
+ // Enable Alpha Testing, Enable Depth Write
|
|
|
|
+ engine.setAlphaTesting(true);
|
|
|
|
+ engine.setDepthWrite(true);
|
|
|
|
+ // For each different model of primitive to render
|
|
|
|
+ context = new BABYLON.Render2DContext(BABYLON.Render2DContext.RenderModeAlphaTest);
|
|
|
|
+ this._renderableData._renderGroupInstancesInfo.forEach(function (k, v) {
|
|
|
|
+ // Prepare the context object, update the WebGL Instanced Array buffer if needed
|
|
|
|
+ var renderCount = _this._prepareContext(engine, context, v);
|
|
|
|
+ // If null is returned, there's no opaque data to render
|
|
|
|
+ if (renderCount === null) {
|
|
|
|
+ return;
|
|
}
|
|
}
|
|
// Submit render only if we have something to render (everything may be hidden and the floatarray empty)
|
|
// Submit render only if we have something to render (everything may be hidden and the floatarray empty)
|
|
- if (!_this.owner.supportInstancedArray || totalRenderCount_1 > 0) {
|
|
|
|
|
|
+ if (!_this.owner.supportInstancedArray || renderCount > 0) {
|
|
// render all the instances of this model, if the render method returns true then our instances are no longer dirty
|
|
// render all the instances of this model, if the render method returns true then our instances are no longer dirty
|
|
- var renderFailed = !v._modelCache.render(v, context);
|
|
|
|
|
|
+ var renderFailed = !v.modelRenderCache.render(v, context);
|
|
// Update dirty flag/related
|
|
// Update dirty flag/related
|
|
- v._dirtyInstancesData = renderFailed;
|
|
|
|
|
|
+ v.opaqueDirty = renderFailed;
|
|
failedCount += renderFailed ? 1 : 0;
|
|
failedCount += renderFailed ? 1 : 0;
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
+ // =======================================================================
|
|
|
|
+ // Third pass, transparent primitive rendering
|
|
|
|
+ // Enable Alpha Testing, Disable Depth Write
|
|
|
|
+ engine.setAlphaTesting(true);
|
|
|
|
+ engine.setDepthWrite(false);
|
|
|
|
+ // First Check if the transparent List change so we can update the TransparentSegment and PartData (sort if needed)
|
|
|
|
+ if (this._renderableData._transparentListChanged) {
|
|
|
|
+ this._updateTransparentData();
|
|
|
|
+ }
|
|
|
|
+ // From this point on we have up to date data to render, so let's go
|
|
|
|
+ failedCount += this._renderTransparentData();
|
|
|
|
+ // =======================================================================
|
|
|
|
+ // Unbind target/restore viewport setting, clear dirty flag, and quit
|
|
// The group's content is no longer dirty
|
|
// The group's content is no longer dirty
|
|
this._cacheGroupDirty = failedCount !== 0;
|
|
this._cacheGroupDirty = failedCount !== 0;
|
|
if (this.isCachedGroup) {
|
|
if (this.isCachedGroup) {
|
|
@@ -335,58 +346,210 @@ var BABYLON;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
+ Group2D.prototype._updateTransparentData = function () {
|
|
|
|
+ var rd = this._renderableData;
|
|
|
|
+ // If null, there was no change of ZOrder, we have nothing to do
|
|
|
|
+ if (rd._firstChangedPrim === null) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ // Sort all the primitive from their depth, max (bottom) to min (top)
|
|
|
|
+ rd._transparentPrimitives.sort(function (a, b) { return b._primitive.getActualZOffset() - a._primitive.getActualZOffset(); });
|
|
|
|
+ var checkAndAddPrimInSegment = function (seg, tpiI) {
|
|
|
|
+ var tpi = rd._transparentPrimitives[tpiI];
|
|
|
|
+ // Fast rejection: if gii are different
|
|
|
|
+ if (seg.groupInsanceInfo !== tpi._groupInstanceInfo) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ var tpiZ = tpi._primitive.getActualZOffset();
|
|
|
|
+ // We've made it so far, the tpi can be part of the segment, add it
|
|
|
|
+ tpi._transparentSegment = seg;
|
|
|
|
+ // Check if we have to update endZ, a smaller value means one above the current one
|
|
|
|
+ if (tpiZ < seg.endZ) {
|
|
|
|
+ seg.endZ = tpiZ;
|
|
|
|
+ seg.endDataIndex = tpi._primitive._getLastIndexInDataBuffer() + 1; // Still exclusive
|
|
|
|
+ }
|
|
|
|
+ return true;
|
|
|
|
+ };
|
|
|
|
+ rd._transparentSegments.splice(0);
|
|
|
|
+ var prevSeg = null;
|
|
|
|
+ for (var tpiI = 0; tpiI < rd._transparentPrimitives.length; tpiI++) {
|
|
|
|
+ var tpi = rd._transparentPrimitives[tpiI];
|
|
|
|
+ // Check if the Data in which the primitive is stored is not sorted properly
|
|
|
|
+ if (tpi._groupInstanceInfo.transparentOrderDirty) {
|
|
|
|
+ tpi._groupInstanceInfo.sortTransparentData();
|
|
|
|
+ }
|
|
|
|
+ // Reset the segment, we have to create/rebuild it
|
|
|
|
+ tpi._transparentSegment = null;
|
|
|
|
+ // If there's a previous valid segment, check if this prim can be part of it
|
|
|
|
+ if (prevSeg) {
|
|
|
|
+ checkAndAddPrimInSegment(prevSeg, tpiI);
|
|
|
|
+ }
|
|
|
|
+ // If we couldn't insert in the adjacent segments, he have to create one
|
|
|
|
+ if (!tpi._transparentSegment) {
|
|
|
|
+ var ts = new BABYLON.TransparentSegment();
|
|
|
|
+ ts.groupInsanceInfo = tpi._groupInstanceInfo;
|
|
|
|
+ var prim = tpi._primitive;
|
|
|
|
+ ts.startZ = prim.getActualZOffset();
|
|
|
|
+ ts.startDataIndex = prim._getFirstIndexInDataBuffer();
|
|
|
|
+ ts.endDataIndex = prim._getLastIndexInDataBuffer() + 1; // Make it exclusive, more natural to use in a for loop
|
|
|
|
+ ts.endZ = ts.startZ;
|
|
|
|
+ tpi._transparentSegment = ts;
|
|
|
|
+ rd._transparentSegments.push(ts);
|
|
|
|
+ }
|
|
|
|
+ // Update prevSeg
|
|
|
|
+ prevSeg = tpi._transparentSegment;
|
|
|
|
+ }
|
|
|
|
+ rd._firstChangedPrim = null;
|
|
|
|
+ rd._transparentListChanged = false;
|
|
|
|
+ };
|
|
|
|
+ Group2D.prototype._renderTransparentData = function () {
|
|
|
|
+ var failedCount = 0;
|
|
|
|
+ var context = new BABYLON.Render2DContext(BABYLON.Render2DContext.RenderModeTransparent);
|
|
|
|
+ var rd = this._renderableData;
|
|
|
|
+ var length = rd._transparentSegments.length;
|
|
|
|
+ for (var i = 0; i < length; i++) {
|
|
|
|
+ var ts = rd._transparentSegments[i];
|
|
|
|
+ var gii = ts.groupInsanceInfo;
|
|
|
|
+ var mrc = gii.modelRenderCache;
|
|
|
|
+ context.useInstancing = false;
|
|
|
|
+ context.partDataStartIndex = ts.startDataIndex;
|
|
|
|
+ context.partDataEndIndex = ts.endDataIndex;
|
|
|
|
+ context.groupInfoPartData = gii.transparentData;
|
|
|
|
+ var renderFailed = !mrc.render(gii, context);
|
|
|
|
+ failedCount += renderFailed ? 1 : 0;
|
|
|
|
+ }
|
|
|
|
+ return failedCount;
|
|
|
|
+ };
|
|
|
|
+ Group2D.prototype._prepareContext = function (engine, context, gii) {
|
|
|
|
+ var gipd = null;
|
|
|
|
+ var setDirty;
|
|
|
|
+ var getDirty;
|
|
|
|
+ // Render Mode specifics
|
|
|
|
+ switch (context.renderMode) {
|
|
|
|
+ case BABYLON.Render2DContext.RenderModeOpaque:
|
|
|
|
+ {
|
|
|
|
+ if (!gii.hasOpaqueData) {
|
|
|
|
+ return null;
|
|
|
|
+ }
|
|
|
|
+ setDirty = function (dirty) { gii.opaqueDirty = dirty; };
|
|
|
|
+ getDirty = function () { return gii.opaqueDirty; };
|
|
|
|
+ context.groupInfoPartData = gii.opaqueData;
|
|
|
|
+ gipd = gii.opaqueData;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ case BABYLON.Render2DContext.RenderModeAlphaTest:
|
|
|
|
+ {
|
|
|
|
+ if (!gii.hasAlphaTestData) {
|
|
|
|
+ return null;
|
|
|
|
+ }
|
|
|
|
+ setDirty = function (dirty) { gii.alphaTestDirty = dirty; };
|
|
|
|
+ getDirty = function () { return gii.alphaTestDirty; };
|
|
|
|
+ context.groupInfoPartData = gii.alphaTestData;
|
|
|
|
+ gipd = gii.alphaTestData;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ default:
|
|
|
|
+ throw new Error("_prepareContext is only for opaque or alphaTest");
|
|
|
|
+ }
|
|
|
|
+ var renderCount = 0;
|
|
|
|
+ // This part will pack the dynamicfloatarray and update the instanced array WebGLBufffer
|
|
|
|
+ // Skip it if instanced arrays are not supported
|
|
|
|
+ if (this.owner.supportInstancedArray) {
|
|
|
|
+ // Flag for instancing
|
|
|
|
+ context.useInstancing = true;
|
|
|
|
+ // Make sure all the WebGLBuffers of the Instanced Array are created/up to date for the parts to render.
|
|
|
|
+ for (var i = 0; i < gipd.length; i++) {
|
|
|
|
+ var pid = gipd[i];
|
|
|
|
+ // If the instances of the model was changed, pack the data
|
|
|
|
+ var array = pid._partData;
|
|
|
|
+ var instanceData_1 = array.pack();
|
|
|
|
+ renderCount += array.usedElementCount;
|
|
|
|
+ // Compute the size the instance buffer should have
|
|
|
|
+ var neededSize = array.usedElementCount * array.stride * 4;
|
|
|
|
+ // Check if we have to (re)create the instancesBuffer because there's none or the size is too small
|
|
|
|
+ if (!pid._partBuffer || (pid._partBufferSize < neededSize)) {
|
|
|
|
+ if (pid._partBuffer) {
|
|
|
|
+ engine.deleteInstancesBuffer(pid._partBuffer);
|
|
|
|
+ }
|
|
|
|
+ pid._partBuffer = engine.createInstancesBuffer(neededSize); // Create + bind
|
|
|
|
+ pid._partBufferSize = neededSize;
|
|
|
|
+ setDirty(false);
|
|
|
|
+ // Update the WebGL buffer to match the new content of the instances data
|
|
|
|
+ engine.updateArrayBuffer(instanceData_1);
|
|
|
|
+ }
|
|
|
|
+ else if (getDirty()) {
|
|
|
|
+ // Update the WebGL buffer to match the new content of the instances data
|
|
|
|
+ engine.bindArrayBuffer(pid._partBuffer);
|
|
|
|
+ engine.updateArrayBuffer(instanceData_1);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ setDirty(false);
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ context.partDataStartIndex = 0;
|
|
|
|
+ // Find the first valid object to get the count
|
|
|
|
+ var i = 0;
|
|
|
|
+ while (!context.groupInfoPartData[i]) {
|
|
|
|
+ i++;
|
|
|
|
+ }
|
|
|
|
+ context.partDataEndIndex = context.groupInfoPartData[i]._partData.usedElementCount;
|
|
|
|
+ }
|
|
|
|
+ return renderCount;
|
|
|
|
+ };
|
|
Group2D.prototype._bindCacheTarget = function () {
|
|
Group2D.prototype._bindCacheTarget = function () {
|
|
var curWidth;
|
|
var curWidth;
|
|
var curHeight;
|
|
var curHeight;
|
|
- if (this._cacheNode) {
|
|
|
|
- var size = this._cacheNode.contentSize;
|
|
|
|
|
|
+ var rd = this._renderableData;
|
|
|
|
+ if (rd._cacheNode) {
|
|
|
|
+ var size = rd._cacheNode.contentSize;
|
|
var groupWidth = Math.ceil(this.actualSize.width);
|
|
var groupWidth = Math.ceil(this.actualSize.width);
|
|
var groupHeight = Math.ceil(this.actualSize.height);
|
|
var groupHeight = Math.ceil(this.actualSize.height);
|
|
if ((size.width < groupWidth) || (size.height < groupHeight)) {
|
|
if ((size.width < groupWidth) || (size.height < groupHeight)) {
|
|
curWidth = Math.floor(size.width * 1.07); // Grow 5% more to avoid frequent resizing for few pixels...
|
|
curWidth = Math.floor(size.width * 1.07); // Grow 5% more to avoid frequent resizing for few pixels...
|
|
curHeight = Math.floor(size.height * 1.07);
|
|
curHeight = Math.floor(size.height * 1.07);
|
|
//console.log(`[${this._globalTransformProcessStep}] Resize group ${this.id}, width: ${curWidth}, height: ${curHeight}`);
|
|
//console.log(`[${this._globalTransformProcessStep}] Resize group ${this.id}, width: ${curWidth}, height: ${curHeight}`);
|
|
- this._cacheTexture.freeRect(this._cacheNode);
|
|
|
|
- this._cacheNode = null;
|
|
|
|
|
|
+ rd._cacheTexture.freeRect(rd._cacheNode);
|
|
|
|
+ rd._cacheNode = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- if (!this._cacheNode) {
|
|
|
|
|
|
+ if (!rd._cacheNode) {
|
|
// Check if we have to allocate a rendering zone in the global cache texture
|
|
// Check if we have to allocate a rendering zone in the global cache texture
|
|
var res = this.owner._allocateGroupCache(this, this.renderGroup, curWidth ? new BABYLON.Size(curWidth, curHeight) : null);
|
|
var res = this.owner._allocateGroupCache(this, this.renderGroup, curWidth ? new BABYLON.Size(curWidth, curHeight) : null);
|
|
- this._cacheNode = res.node;
|
|
|
|
- this._cacheTexture = res.texture;
|
|
|
|
- this._cacheRenderSprite = res.sprite;
|
|
|
|
- var size = this._cacheNode.contentSize;
|
|
|
|
|
|
+ rd._cacheNode = res.node;
|
|
|
|
+ rd._cacheTexture = res.texture;
|
|
|
|
+ rd._cacheRenderSprite = res.sprite;
|
|
|
|
+ var size = rd._cacheNode.contentSize;
|
|
}
|
|
}
|
|
- var n = this._cacheNode;
|
|
|
|
- this._cacheTexture.bindTextureForPosSize(n.pos, this.actualSize, true);
|
|
|
|
|
|
+ var n = rd._cacheNode;
|
|
|
|
+ rd._cacheTexture.bindTextureForPosSize(n.pos, this.actualSize, true);
|
|
};
|
|
};
|
|
Group2D.prototype._unbindCacheTarget = function () {
|
|
Group2D.prototype._unbindCacheTarget = function () {
|
|
- if (this._cacheTexture) {
|
|
|
|
- this._cacheTexture.unbindTexture();
|
|
|
|
|
|
+ if (this._renderableData._cacheTexture) {
|
|
|
|
+ this._renderableData._cacheTexture.unbindTexture();
|
|
}
|
|
}
|
|
};
|
|
};
|
|
Group2D.prototype.handleGroupChanged = function (prop) {
|
|
Group2D.prototype.handleGroupChanged = function (prop) {
|
|
// This method is only for cachedGroup
|
|
// This method is only for cachedGroup
|
|
- if (!this.isCachedGroup || !this._cacheRenderSprite) {
|
|
|
|
|
|
+ var rd = this._renderableData;
|
|
|
|
+ if (!this.isCachedGroup || !rd._cacheRenderSprite) {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
// For now we only support these property changes
|
|
// For now we only support these property changes
|
|
// TODO: add more! :)
|
|
// TODO: add more! :)
|
|
if (prop.id === BABYLON.Prim2DBase.positionProperty.id) {
|
|
if (prop.id === BABYLON.Prim2DBase.positionProperty.id) {
|
|
- this._cacheRenderSprite.position = this.position.clone();
|
|
|
|
|
|
+ rd._cacheRenderSprite.position = this.position.clone();
|
|
}
|
|
}
|
|
else if (prop.id === BABYLON.Prim2DBase.rotationProperty.id) {
|
|
else if (prop.id === BABYLON.Prim2DBase.rotationProperty.id) {
|
|
- this._cacheRenderSprite.rotation = this.rotation;
|
|
|
|
|
|
+ rd._cacheRenderSprite.rotation = this.rotation;
|
|
}
|
|
}
|
|
else if (prop.id === BABYLON.Prim2DBase.scaleProperty.id) {
|
|
else if (prop.id === BABYLON.Prim2DBase.scaleProperty.id) {
|
|
- this._cacheRenderSprite.scale = this.scale;
|
|
|
|
|
|
+ rd._cacheRenderSprite.scale = this.scale;
|
|
}
|
|
}
|
|
else if (prop.id === BABYLON.Prim2DBase.originProperty.id) {
|
|
else if (prop.id === BABYLON.Prim2DBase.originProperty.id) {
|
|
- this._cacheRenderSprite.origin = this.origin.clone();
|
|
|
|
|
|
+ rd._cacheRenderSprite.origin = this.origin.clone();
|
|
}
|
|
}
|
|
else if (prop.id === Group2D.actualSizeProperty.id) {
|
|
else if (prop.id === Group2D.actualSizeProperty.id) {
|
|
- this._cacheRenderSprite.spriteSize = this.actualSize.clone();
|
|
|
|
|
|
+ rd._cacheRenderSprite.spriteSize = this.actualSize.clone();
|
|
}
|
|
}
|
|
};
|
|
};
|
|
Group2D.prototype.detectGroupStates = function () {
|
|
Group2D.prototype.detectGroupStates = function () {
|
|
@@ -434,12 +597,15 @@ var BABYLON;
|
|
this._isCachedGroup = true;
|
|
this._isCachedGroup = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+ if (this._isRenderableGroup) {
|
|
|
|
+ this._renderableData = new RenderableGroupData();
|
|
|
|
+ }
|
|
// If the group is tagged as renderable we add it to the renderable tree
|
|
// If the group is tagged as renderable we add it to the renderable tree
|
|
if (this._isCachedGroup) {
|
|
if (this._isCachedGroup) {
|
|
var cur = this.parent;
|
|
var cur = this.parent;
|
|
while (cur) {
|
|
while (cur) {
|
|
if (cur instanceof Group2D && cur._isRenderableGroup) {
|
|
if (cur instanceof Group2D && cur._isRenderableGroup) {
|
|
- cur._childrenRenderableGroups.push(this);
|
|
|
|
|
|
+ cur._renderableData._childrenRenderableGroups.push(this);
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
cur = cur.parent;
|
|
cur = cur.parent;
|
|
@@ -470,6 +636,77 @@ var BABYLON;
|
|
BABYLON.className("Group2D")
|
|
BABYLON.className("Group2D")
|
|
], Group2D);
|
|
], Group2D);
|
|
return Group2D;
|
|
return Group2D;
|
|
- }(BABYLON.Prim2DBase));
|
|
|
|
|
|
+ })(BABYLON.Prim2DBase);
|
|
BABYLON.Group2D = Group2D;
|
|
BABYLON.Group2D = Group2D;
|
|
|
|
+ var RenderableGroupData = (function () {
|
|
|
|
+ function RenderableGroupData() {
|
|
|
|
+ this._primDirtyList = new Array();
|
|
|
|
+ this._childrenRenderableGroups = new Array();
|
|
|
|
+ this._renderGroupInstancesInfo = new BABYLON.StringDictionary();
|
|
|
|
+ this._transparentPrimitives = new Array();
|
|
|
|
+ this._transparentSegments = new Array();
|
|
|
|
+ this._firstChangedPrim = null;
|
|
|
|
+ this._transparentListChanged = false;
|
|
|
|
+ }
|
|
|
|
+ RenderableGroupData.prototype.dispose = function () {
|
|
|
|
+ if (this._cacheRenderSprite) {
|
|
|
|
+ this._cacheRenderSprite.dispose();
|
|
|
|
+ this._cacheRenderSprite = null;
|
|
|
|
+ }
|
|
|
|
+ if (this._cacheTexture && this._cacheNode) {
|
|
|
|
+ this._cacheTexture.freeRect(this._cacheNode);
|
|
|
|
+ this._cacheTexture = null;
|
|
|
|
+ this._cacheNode = null;
|
|
|
|
+ }
|
|
|
|
+ if (this._primDirtyList) {
|
|
|
|
+ this._primDirtyList.splice(0);
|
|
|
|
+ this._primDirtyList = null;
|
|
|
|
+ }
|
|
|
|
+ if (this._renderGroupInstancesInfo) {
|
|
|
|
+ this._renderGroupInstancesInfo.forEach(function (k, v) {
|
|
|
|
+ v.dispose();
|
|
|
|
+ });
|
|
|
|
+ this._renderGroupInstancesInfo = null;
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ RenderableGroupData.prototype.addNewTransparentPrimitiveInfo = function (prim, gii) {
|
|
|
|
+ var tpi = new TransparentPrimitiveInfo();
|
|
|
|
+ tpi._primitive = prim;
|
|
|
|
+ tpi._groupInstanceInfo = gii;
|
|
|
|
+ tpi._transparentSegment = null;
|
|
|
|
+ this._transparentPrimitives.push(tpi);
|
|
|
|
+ this._transparentListChanged = true;
|
|
|
|
+ this.updateSmallestZChangedPrim(tpi);
|
|
|
|
+ return tpi;
|
|
|
|
+ };
|
|
|
|
+ RenderableGroupData.prototype.removeTransparentPrimitiveInfo = function (tpi) {
|
|
|
|
+ var index = this._transparentPrimitives.indexOf(tpi);
|
|
|
|
+ if (index !== -1) {
|
|
|
|
+ this._transparentPrimitives.splice(index, 1);
|
|
|
|
+ this._transparentListChanged = true;
|
|
|
|
+ this.updateSmallestZChangedPrim(tpi);
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ RenderableGroupData.prototype.transparentPrimitiveZChanged = function (tpi) {
|
|
|
|
+ this._transparentListChanged = true;
|
|
|
|
+ this.updateSmallestZChangedPrim(tpi);
|
|
|
|
+ };
|
|
|
|
+ RenderableGroupData.prototype.updateSmallestZChangedPrim = function (tpi) {
|
|
|
|
+ if (tpi._primitive) {
|
|
|
|
+ var newZ = tpi._primitive.getActualZOffset();
|
|
|
|
+ var curZ = this._firstChangedPrim ? this._firstChangedPrim._primitive.getActualZOffset() : Number.MIN_VALUE;
|
|
|
|
+ if (newZ > curZ) {
|
|
|
|
+ this._firstChangedPrim = tpi;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ return RenderableGroupData;
|
|
|
|
+ })();
|
|
|
|
+ BABYLON.RenderableGroupData = RenderableGroupData;
|
|
|
|
+ var TransparentPrimitiveInfo = (function () {
|
|
|
|
+ function TransparentPrimitiveInfo() {
|
|
|
|
+ }
|
|
|
|
+ return TransparentPrimitiveInfo;
|
|
|
|
+ })();
|
|
|
|
+ BABYLON.TransparentPrimitiveInfo = TransparentPrimitiveInfo;
|
|
})(BABYLON || (BABYLON = {}));
|
|
})(BABYLON || (BABYLON = {}));
|