Compare commits

...

4 Commits

Author SHA1 Message Date
Adam Beili 03b89c386b
Merge 767c1a8c7d into ee2b3813b2 2025-11-22 10:23:28 +08:00
jjspace ee2b3813b2
Merge pull request #13038 from CesiumGS/fix/textureatlas-border-internal
deploy / deploy (push) Has been cancelled Details
dev / lint (push) Has been cancelled Details
dev / coverage (push) Has been cancelled Details
dev / release-tests (push) Has been cancelled Details
dev / node-20 (push) Has been cancelled Details
sandcastle-dev / deploy (push) Has been cancelled Details
fix(textureatlas): Apply internal padding between images
2025-11-21 17:14:38 +00:00
Don McCurdy 4e3980cc53 fix(textureatlas): Apply internal padding between images 2025-11-21 11:44:18 -05:00
Adam 767c1a8c7d Introduce entity.asynchronous flag 2025-11-07 16:08:50 +02:00
6 changed files with 187 additions and 37 deletions

View File

@ -7,7 +7,8 @@
#### Fixes :wrench: #### Fixes :wrench:
- Billboards using `imageSubRegion` now render as expected. [#12585](https://github.com/CesiumGS/cesium/issues/12585) - Billboards using `imageSubRegion` now render as expected. [#12585](https://github.com/CesiumGS/cesium/issues/12585)
- Improved scaling of SVGs in billboards [#TODO](https://github.com/CesiumGS/cesium/issues/TODO) - Improved scaling of SVGs in billboards [#13020](https://github.com/CesiumGS/cesium/issues/13020)
- Fixed unexpected outline artifacts around billboards [#13038](https://github.com/CesiumGS/cesium/issues/13038)
#### Additions :tada: #### Additions :tada:

View File

@ -122,6 +122,8 @@ TexturePacker.prototype._findNode = function (node, { width, height }) {
return node; return node;
} }
const borderPadding = this._borderPadding;
// Vertical split (childNode1 = left half, childNode2 = right half). // Vertical split (childNode1 = left half, childNode2 = right half).
if (widthDifference > heightDifference) { if (widthDifference > heightDifference) {
node.childNode1 = new TextureNode({ node.childNode1 = new TextureNode({
@ -130,12 +132,18 @@ TexturePacker.prototype._findNode = function (node, { width, height }) {
width, width,
height: nodeHeight, height: nodeHeight,
}); });
node.childNode2 = new TextureNode({
x: rectangle.x + width, // Apply padding only along the vertical "cut".
y: rectangle.y, const widthDifferencePadded = widthDifference - borderPadding;
width: widthDifference,
height: nodeHeight, if (widthDifferencePadded > 0) {
}); node.childNode2 = new TextureNode({
x: rectangle.x + width + borderPadding,
y: rectangle.y,
width: widthDifferencePadded,
height: nodeHeight,
});
}
return this._findNode(node.childNode1, { width, height }); return this._findNode(node.childNode1, { width, height });
} }
@ -147,12 +155,19 @@ TexturePacker.prototype._findNode = function (node, { width, height }) {
width: nodeWidth, width: nodeWidth,
height, height,
}); });
node.childNode2 = new TextureNode({
x: rectangle.x, // Apply padding only along the horizontal "cut".
y: rectangle.y + height, const heightDifferencePadded = heightDifference - borderPadding;
width: nodeWidth,
height: heightDifference, if (heightDifferencePadded > 0) {
}); node.childNode2 = new TextureNode({
x: rectangle.x,
y: rectangle.y + height + borderPadding,
width: nodeWidth,
height: heightDifferencePadded,
});
}
return this._findNode(node.childNode1, { width, height }); return this._findNode(node.childNode1, { width, height });
} }

View File

@ -98,6 +98,7 @@ function createPropertyTypeDescriptor(name, Type) {
* @property {PolylineVolumeGraphics | PolylineVolumeGraphics.ConstructorOptions} [polylineVolume] A polylineVolume to associate with this entity. * @property {PolylineVolumeGraphics | PolylineVolumeGraphics.ConstructorOptions} [polylineVolume] A polylineVolume to associate with this entity.
* @property {RectangleGraphics | RectangleGraphics.ConstructorOptions} [rectangle] A rectangle to associate with this entity. * @property {RectangleGraphics | RectangleGraphics.ConstructorOptions} [rectangle] A rectangle to associate with this entity.
* @property {WallGraphics | WallGraphics.ConstructorOptions} [wall] A wall to associate with this entity. * @property {WallGraphics | WallGraphics.ConstructorOptions} [wall] A wall to associate with this entity.
* @property {boolean} [asynchronous=true] Determines if the underlying primitives will be created asynchronously or block until ready.
*/ */
/** /**
@ -121,6 +122,7 @@ function Entity(options) {
this._availability = undefined; this._availability = undefined;
this._id = id; this._id = id;
this._asynchronous = options.asynchronous ?? true;
this._definitionChanged = new Event(); this._definitionChanged = new Event();
this._name = options.name; this._name = options.name;
this._show = options.show ?? true; this._show = options.show ?? true;
@ -249,6 +251,16 @@ Object.defineProperties(Entity.prototype, {
return this._id; return this._id;
}, },
}, },
/**
* Gets whether the underlying primitives should be created asynchronously or block until ready.
* @memberof Entity.prototype
* @type {boolean}
*/
asynchronous: {
get: function () {
return this._asynchronous;
},
},
/** /**
* Gets the event that is raised whenever a property or sub-property is changed or modified. * Gets the event that is raised whenever a property or sub-property is changed or modified.
* @memberof Entity.prototype * @memberof Entity.prototype

View File

@ -50,6 +50,7 @@ function GeometryUpdater(options) {
const geometryPropertyName = options.geometryPropertyName; const geometryPropertyName = options.geometryPropertyName;
this._entity = entity; this._entity = entity;
this._asynchronous = entity.asynchronous;
this._scene = options.scene; this._scene = options.scene;
this._fillEnabled = false; this._fillEnabled = false;
this._isClosed = false; this._isClosed = false;
@ -242,6 +243,19 @@ Object.defineProperties(GeometryUpdater.prototype, {
return this._dynamic; return this._dynamic;
}, },
}, },
/**
* Gets a value indicating if the geometry should be created asynchronously
*
* @memberof GeometryUpdater.prototype
*
* @type {boolean}
* @readonly
*/
asynchronous: {
get: function () {
return this._asynchronous;
},
},
/** /**
* Gets a value indicating if the geometry is closed. * Gets a value indicating if the geometry is closed.
* This property is only valid for static geometry. * This property is only valid for static geometry.

View File

@ -27,6 +27,7 @@ function Batch(
depthFailMaterialProperty, depthFailMaterialProperty,
closed, closed,
shadows, shadows,
asynchronous,
) { ) {
this.translucent = translucent; this.translucent = translucent;
this.appearanceType = appearanceType; this.appearanceType = appearanceType;
@ -48,6 +49,7 @@ function Batch(
this.showsUpdated = new AssociativeArray(); this.showsUpdated = new AssociativeArray();
this.itemsToRemove = []; this.itemsToRemove = [];
this.invalidated = false; this.invalidated = false;
this.asynchronous = asynchronous ?? true;
let removeMaterialSubscription; let removeMaterialSubscription;
if (defined(depthFailMaterialProperty)) { if (defined(depthFailMaterialProperty)) {
@ -156,7 +158,7 @@ Batch.prototype.update = function (time) {
primitive = new Primitive({ primitive = new Primitive({
show: false, show: false,
asynchronous: true, asynchronous: this.asynchronous,
geometryInstances: geometries.slice(), geometryInstances: geometries.slice(),
appearance: new this.appearanceType({ appearance: new this.appearanceType({
translucent: this.translucent, translucent: this.translucent,
@ -430,7 +432,10 @@ StaticGeometryColorBatch.prototype.add = function (time, updater) {
const length = items.length; const length = items.length;
for (let i = 0; i < length; i++) { for (let i = 0; i < length; i++) {
const item = items[i]; const item = items[i];
if (item.isMaterial(updater)) { if (
item.isMaterial(updater) &&
item.asynchronous === updater.asynchronous
) {
item.add(updater, instance); item.add(updater, instance);
return; return;
} }
@ -443,6 +448,7 @@ StaticGeometryColorBatch.prototype.add = function (time, updater) {
updater.depthFailMaterialProperty, updater.depthFailMaterialProperty,
this._closed, this._closed,
this._shadows, this._shadows,
updater.asynchronous,
); );
batch.add(updater, instance); batch.add(updater, instance);
items.push(batch); items.push(batch);

View File

@ -852,16 +852,16 @@ describe("Scene/TextureAtlas", function () {
.2222222222222222............... .2222222222222222...............
.2222222222222222............... .2222222222222222...............
.2222222222222222............... .2222222222222222...............
.22222222222222223333333333..... .2222222222222222.3333333333....
.22222222222222223333333333..... .2222222222222222.3333333333....
.22222222222222223333333333..... .2222222222222222.3333333333....
.22222222222222223333333333..... .2222222222222222.3333333333....
.22222222222222223333333333..... .2222222222222222.3333333333....
.22222222222222223333333333..... .2222222222222222.3333333333....
.22222222222222223333333333..... .2222222222222222.3333333333....
.22222222222222223333333333..... .2222222222222222.3333333333.1..
.22222222222222223333333333..... .2222222222222222.3333333333....
.2222222222222222333333333301... .2222222222222222.3333333333.0..
................................ ................................
`.trim(), `.trim(),
); );
@ -926,9 +926,9 @@ describe("Scene/TextureAtlas", function () {
.2222222222... .2222222222...
.2222222222... .2222222222...
.2222222222... .2222222222...
.2222222222.1.
.2222222222... .2222222222...
.2222222222... .2222222222.0.
.222222222201.
.............. ..............
`.trim(), `.trim(),
); );
@ -976,16 +976,16 @@ describe("Scene/TextureAtlas", function () {
.3333333333333333............... .3333333333333333...............
.3333333333333333............... .3333333333333333...............
.3333333333333333............... .3333333333333333...............
.33333333333333332222222222..... .3333333333333333.2222222222....
.33333333333333332222222222..... .3333333333333333.2222222222....
.33333333333333332222222222..... .3333333333333333.2222222222....
.33333333333333332222222222..... .3333333333333333.2222222222....
.33333333333333332222222222..... .3333333333333333.2222222222....
.33333333333333332222222222..... .3333333333333333.2222222222....
.33333333333333332222222222..... .3333333333333333.2222222222....
.33333333333333332222222222..... .3333333333333333.2222222222.1..
.33333333333333332222222222..... .3333333333333333.2222222222....
.3333333333333333222222222201... .3333333333333333.2222222222.0..
................................ ................................
`.trim(), `.trim(),
); );
@ -1337,6 +1337,108 @@ describe("Scene/TextureAtlas", function () {
).contextToRender([0, 255, 0, 255]); ).contextToRender([0, 255, 0, 255]);
}); });
it("adds custom padding with borderWidthInPixels", async function () {
atlas = new TextureAtlas({ borderWidthInPixels: 0 });
let indices = await addImages();
expect(drawAtlas(atlas, indices)).toBe(
`
................
................
................
................
................
................
2222222222......
2222222222......
2222222222......
2222222222......
2222222222......
2222222222......
22222222220.....
22222222220.....
22222222220.....
222222222201....
`.trim(),
);
atlas = new TextureAtlas({ borderWidthInPixels: 2 });
indices = await addImages();
expect(drawAtlas(atlas, indices)).toBe(
`
................................
................................
................................
................................
..2222222222....................
..2222222222....................
..2222222222....................
..2222222222..1.................
..2222222222....................
..2222222222....................
..2222222222..0.................
..2222222222..0.................
..2222222222..0.................
..2222222222..0.................
................................
................................
`.trim(),
);
atlas = new TextureAtlas({ borderWidthInPixels: 5 });
indices = await addImages();
expect(drawAtlas(atlas, indices)).toBe(
`
................................
................................
................................
................................
................................
................................
................................
................................
................................
................................
................................
................................
................................
................................
................................
................................
................................
.....2222222222.................
.....2222222222.................
.....2222222222.................
.....2222222222.................
.....2222222222.................
.....2222222222.................
.....2222222222.....0...........
.....2222222222.....0...........
.....2222222222.....0...........
.....2222222222.....0.....1.....
................................
................................
................................
................................
................................
`.trim(),
);
async function addImages() {
const promise = Promise.all([
atlas.addImage(tallGreenGuid, tallGreenImage),
atlas.addImage(blueGuid, blueImage),
atlas.addImage(bigBlueGuid, bigBlueImage),
]);
return pollWhilePromise(promise, () => {
atlas.update(scene.frameState.context);
});
}
});
it("GUID changes when atlas texure is modified", async function () { it("GUID changes when atlas texure is modified", async function () {
atlas = new TextureAtlas(); atlas = new TextureAtlas();