fix: visibleBoundingBox optimizations

This commit is contained in:
cha0s 2019-04-16 13:30:15 -05:00
parent 1b35fc4215
commit 570054c610
4 changed files with 56 additions and 41 deletions

View File

@ -31,8 +31,7 @@ export class Listed extends Trait {
if (!list) { if (!list) {
return; return;
} }
const quadTree = list.quadTree; if (!this.entity.is('visible')) {
if (!('visibleBoundingBox' in this.entity)) {
return; return;
} }
const aabb = this.entity.visibleBoundingBox; const aabb = this.entity.visibleBoundingBox;
@ -45,6 +44,7 @@ export class Listed extends Trait {
const points = Rectangle.toPoints(expandedAabb); const points = Rectangle.toPoints(expandedAabb);
this.quadTreeNodes = points.map((point) => [...point, this.entity]); this.quadTreeNodes = points.map((point) => [...point, this.entity]);
// Add points to quad tree. // Add points to quad tree.
const quadTree = list.quadTree;
for (const node of this.quadTreeNodes) { for (const node of this.quadTreeNodes) {
quadTree.add(node); quadTree.add(node);
} }
@ -84,11 +84,7 @@ export class Listed extends Trait {
listeners() { listeners() {
return { return {
visibleBoundingBoxesUpdated: () => { visibleBoundingBoxChanged: () => {
this.resetQuadTreeNodes();
},
positionChanged: () => {
this.resetQuadTreeNodes(); this.resetQuadTreeNodes();
}, },

View File

@ -11,7 +11,7 @@ const decorate = compose(
}), }),
); );
class PicturedBase extends Trait { export class Pictured extends decorate(Trait) {
static defaultParams() { static defaultParams() {
return { return {
@ -28,8 +28,6 @@ class PicturedBase extends Trait {
initialize() { initialize() {
this._images = this.params.get('images').toJS(); this._images = this.params.get('images').toJS();
this.sprites = undefined; this.sprites = undefined;
// Bounding box update.
this.entity.emit('visibleBoundingBoxesUpdated');
} }
destroy() { destroy() {
@ -61,10 +59,11 @@ class PicturedBase extends Trait {
return; return;
} }
// Load all images. // Load all images.
const imagePromises = [];
this.sprites = {}; this.sprites = {};
for (const key in this._images) { for (const key in this._images) {
const {uri} = this._images[key]; const {uri} = this._images[key];
Image.load(uri).then((image) => { const imagePromise = Image.load(uri).then((image) => {
const sprite = this.sprites[key] = new Sprite(image); const sprite = this.sprites[key] = new Sprite(image);
// Calculate any offset. // Calculate any offset.
sprite.position = this.viewPositionFor(key); sprite.position = this.viewPositionFor(key);
@ -74,7 +73,12 @@ class PicturedBase extends Trait {
this.showImage(key); this.showImage(key);
} }
}); });
imagePromises.push(imagePromise);
} }
Promise.all(imagePromises).then((images) => {
// Bounding box update.
this.entity.updateVisibleBoundingBox();
});
} }
offsetFor(key) { offsetFor(key) {
@ -133,7 +137,7 @@ class PicturedBase extends Trait {
return { return {
currentImageChanged: (oldKey) => { currentImageChanged: (oldKey) => {
// Bounding box update. // Bounding box update.
this.entity.emit('visibleBoundingBoxesUpdated'); this.entity.updateVisibleBoundingBox();
// Only client/graphics. // Only client/graphics.
if (!this.sprites) { if (!this.sprites) {
return; return;
@ -155,5 +159,3 @@ class PicturedBase extends Trait {
} }
} }
export class Pictured extends decorate(PicturedBase) {}

View File

@ -1,6 +1,7 @@
import {compose} from '@avocado/core'; import {compose} from '@avocado/core';
import {StateProperty, Trait} from '@avocado/entity'; import {StateProperty, Trait} from '@avocado/entity';
import {Rectangle, Vector} from '@avocado/math'; import {Rectangle, Vector} from '@avocado/math';
import {Property} from '@avocado/mixins';
import {Container} from '../container'; import {Container} from '../container';
import {hasGraphics} from '../has-graphics'; import {hasGraphics} from '../has-graphics';
@ -9,6 +10,13 @@ const decorate = compose(
StateProperty('isVisible', { StateProperty('isVisible', {
track: true, track: true,
}), }),
Property('visibleBoundingBox', {
default: [0, 0, 0, 0],
emit: function (...args) {
this.entity.emit(...args);
},
track: true,
})
); );
export class Visible extends decorate(Trait) { export class Visible extends decorate(Trait) {
@ -30,8 +38,8 @@ export class Visible extends decorate(Trait) {
this._container = new Container(); this._container = new Container();
this._container.isVisible = this.entity.isVisible; this._container.isVisible = this.entity.isVisible;
} }
this.scheduledBoundingBoxUpdate = true;
this.trackPosition = this.params.get('trackPosition'); this.trackPosition = this.params.get('trackPosition');
this._visibleBoundingBox = [0, 0, 0, 0];
} }
destroy() { destroy() {
@ -44,27 +52,6 @@ export class Visible extends decorate(Trait) {
return this._container; return this._container;
} }
get visibleBoundingBox() {
if (Rectangle.isNull(this._visibleBoundingBox)) {
// Collect all bounding boxes.
const visibleBoundingBoxes = this.entity.invokeHookFlat(
'visibleBoundingBoxes'
);
if (0 === visibleBoundingBoxes.length) {
return [0, 0, 0, 0];
}
let unifiedBoundingBox = [0, 0, 0, 0];
for (const visibleBoundingBox of visibleBoundingBoxes) {
unifiedBoundingBox = Rectangle.united(
unifiedBoundingBox,
visibleBoundingBox,
);
}
this._visibleBoundingBox = unifiedBoundingBox;
}
return this._visibleBoundingBox;
}
shouldSynchronizePosition() { shouldSynchronizePosition() {
return this._container && this.trackPosition; return this._container && this.trackPosition;
} }
@ -84,9 +71,9 @@ export class Visible extends decorate(Trait) {
this.entity.container.visible = this.entity.isVisible; this.entity.container.visible = this.entity.isVisible;
}, },
visibleBoundingBoxesUpdated: () => { positionChanged: () => {
this._visibleBoundingBox = [0, 0, 0, 0]; this.scheduledBoundingBoxUpdate = true;
} },
}; };
if (this.shouldSynchronizePosition()) { if (this.shouldSynchronizePosition()) {
@ -103,10 +90,40 @@ export class Visible extends decorate(Trait) {
return listeners; return listeners;
} }
methods() {
return {
updateVisibleBoundingBox: () => {
this.scheduledBoundingBoxUpdate = true;
},
}
}
tick(elapsed) { tick(elapsed) {
if (this.shouldSynchronizePosition()) { if (this.shouldSynchronizePosition()) {
this.synchronizePosition(); this.synchronizePosition();
} }
if (this.scheduledBoundingBoxUpdate) {
// Collect all bounding boxes.
const visibleBoundingBoxes = this.entity.invokeHookFlat(
'visibleBoundingBoxes'
);
if (0 === visibleBoundingBoxes.length) {
this.visibleBoundingBox = [0, 0, 0, 0];
}
else {
let unifiedBoundingBox = [0, 0, 0, 0];
for (const visibleBoundingBox of visibleBoundingBoxes) {
unifiedBoundingBox = Rectangle.united(
unifiedBoundingBox,
visibleBoundingBox,
);
}
this.visibleBoundingBox = unifiedBoundingBox;
this.scheduledBoundingBoxUpdate = false;
}
}
} }
} }

View File

@ -81,7 +81,7 @@ class AnimatedBase extends Trait {
animation.direction = this.entity.direction; animation.direction = this.entity.direction;
}); });
// Bounding box update. // Bounding box update.
this.entity.emit('visibleBoundingBoxesUpdated'); this.entity.updateVisibleBoundingBox();
}); });
} }
@ -167,7 +167,7 @@ class AnimatedBase extends Trait {
oldAnimation.reset(); oldAnimation.reset();
} }
// Bounding box update. // Bounding box update.
this.entity.emit('visibleBoundingBoxesUpdated'); this.entity.updateVisibleBoundingBox();
// Only client/graphics. // Only client/graphics.
if (!this.animationViews) { if (!this.animationViews) {
return; return;