Skip to content

Commit ec4b67f

Browse files
[FIX] Adopt top-left texture origin (playcanvas#3335)
* start * flip 2d elements, particle systems * enable ImageBitmap * fix examples and update basis example * update comment * remove commented code * disable imagebitmap * Add new line at end * Add new line at end * Add new line at end Co-authored-by: Will Eastcott <will@playcanvas.com>
1 parent 03762bd commit ec4b67f

23 files changed

+112
-109
lines changed
-47.3 KB
Binary file not shown.

examples/assets/textures/sea.basis

-155 KB
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

examples/src/examples/graphics/render-to-texture.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ class RenderToTextureExample extends Example {
130130
// create a plane called tv which we use to display rendered texture
131131
// this is only added to excluded Layer, so it does not render into texture
132132
const tv = createPrimitive("plane", new pc.Vec3(6, 8, -5), new pc.Vec3(20, 10, 10), pc.Color.BLACK, [excludedLayer.id]);
133-
tv.setLocalEulerAngles(90, 0, 0);
133+
tv.setLocalEulerAngles(90, 0, 180);
134134
tv.render.castShadows = false;
135135
tv.render.receiveShadows = false;
136136
// @ts-ignore engine-tsd

examples/src/examples/graphics/texture-basis.tsx

Lines changed: 54 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,21 @@ class TextureBasisExample extends Example {
77
static CATEGORY = 'Graphics';
88
static NAME = 'Texture Basis';
99

10+
// Color textures have been converted with the following arguments:
11+
// basisu seaside-rocks01-gloss.jpg -q 255 -mipmap
12+
// The normalmap has been converted with the following arguments:
13+
// basisu seaside-rocks01-normal.jpg -normal_map -swizzle gggr -renorm -q 255 -mipmap
1014
load() {
1115
return <>
12-
<AssetLoader name='seaBasis' type='texture' url='static/assets/textures/sea.basis' />
13-
<AssetLoader name='playcanvasBasis' type='texture' url='static/assets/textures/playcanvas.basis' />
16+
<AssetLoader name='color' type='texture' url='static/assets/textures/seaside-rocks01-color.basis' />
17+
<AssetLoader name='gloss' type='texture' url='static/assets/textures/seaside-rocks01-gloss.basis' />
18+
<AssetLoader name='normal' type='texture' url='static/assets/textures/seaside-rocks01-normal.basis' data={{ type: pc.TEXTURETYPE_SWIZZLEGGGR }} />
19+
<AssetLoader name='helipad' type='cubemap' url='static/assets/cubemaps/helipad.dds' data={{ type: pc.TEXTURETYPE_RGBM }}/>
1420
</>;
1521
}
1622

1723
// @ts-ignore: override class function
18-
example(canvas: HTMLCanvasElement, assets: { seaBasis: pc.Asset, playcanvasBasis: pc.Asset }): void {
24+
example(canvas: HTMLCanvasElement, assets: { color: pc.Asset, gloss: pc.Asset, normal: pc.Asset, helipad: pc.Asset }): void {
1925

2026
// Create the application and start the update loop
2127
const app = new pc.Application(canvas, {});
@@ -33,30 +39,48 @@ class TextureBasisExample extends Example {
3339
app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW);
3440
app.setCanvasResolution(pc.RESOLUTION_AUTO);
3541

36-
app.scene.ambientLight = new pc.Color(1, 1, 1);
37-
38-
// material using basis texture
39-
const material1 = new pc.StandardMaterial();
40-
material1.diffuseMap = assets.seaBasis.resource;
41-
material1.update();
42-
43-
// Create a Entity with a Box render component
44-
const box1 = new pc.Entity();
45-
box1.addComponent("render", {
46-
type: "box",
47-
material: material1
42+
// Set skybox
43+
app.scene.gammaCorrection = pc.GAMMA_SRGB;
44+
app.scene.toneMapping = pc.TONEMAP_ACES;
45+
app.scene.skyboxMip = 1;
46+
app.scene.skyboxIntensity = 0.7;
47+
app.scene.setSkybox(assets.helipad.resources);
48+
49+
// Create directional light
50+
const light = new pc.Entity();
51+
light.addComponent('light', {
52+
type: 'directional'
4853
});
49-
50-
// another material using basis texture
51-
const material2 = new pc.StandardMaterial();
52-
material2.diffuseMap = assets.playcanvasBasis.resource;
53-
material2.update();
54-
55-
const box2 = new pc.Entity();
56-
box2.addComponent("render", {
57-
type: "box",
58-
material: material2
54+
light.setLocalEulerAngles(45, 0, 45);
55+
56+
// Construct material
57+
const material = new pc.StandardMaterial();
58+
material.useMetalness = true;
59+
material.diffuse = new pc.Color(0.3, 0.3, 0.3);
60+
material.shininess = 80;
61+
material.metalness = 0.7;
62+
material.diffuseMap = assets.color.resource;
63+
material.normalMap = assets.normal.resource;
64+
material.glossMap = assets.gloss.resource;
65+
material.diffuseMapTiling.set(7, 7);
66+
material.normalMapTiling.set(7, 7);
67+
material.glossMapTiling.set(7, 7);
68+
material.update();
69+
70+
// Create a torus shape
71+
const torus = pc.createTorus(app.graphicsDevice, {
72+
tubeRadius: 0.2,
73+
ringRadius: 0.3,
74+
segments: 50,
75+
sides: 40
76+
});
77+
const shape = new pc.Entity();
78+
shape.addComponent('render', {
79+
material: material,
80+
meshInstances: [new pc.MeshInstance(torus, material)]
5981
});
82+
shape.setPosition(0, 0, 0);
83+
shape.setLocalScale(2, 2, 2);
6084

6185
// Create an Entity with a camera component
6286
const camera = new pc.Entity();
@@ -65,27 +89,20 @@ class TextureBasisExample extends Example {
6589
});
6690

6791
// Adjust the camera position
68-
camera.translate(0, 0, 5);
92+
camera.translate(0, 0, 4);
6993

7094
// Add the new Entities to the hierarchy
71-
app.root.addChild(box1);
72-
app.root.addChild(box2);
95+
app.root.addChild(light);
96+
app.root.addChild(shape);
7397
app.root.addChild(camera);
7498

75-
box1.setPosition(0, -1, 0);
76-
box2.setPosition(0, 1, 0);
77-
7899
// Set an update function on the app's update event
79100
let angle = 0;
80101
app.on("update", function (dt) {
81-
angle += dt;
82-
if (angle > 360) {
83-
angle = 0;
84-
}
102+
angle = (angle + dt * 10) % 360;
85103

86104
// Rotate the boxes
87-
box1.setEulerAngles(angle * 2, angle * 4, angle * 8);
88-
box2.setEulerAngles(90 - angle * 12, 120 - angle * 8, 150 - angle * 10);
105+
shape.setEulerAngles(angle, angle * 2, angle * 4);
89106
});
90107
}
91108
}

src/framework/components/element/image-element.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -406,26 +406,26 @@ class ImageElement {
406406
// POS: 0, 0, 0
407407
vertexDataF32[5] = 1; // NZ
408408
vertexDataF32[6] = r.x; // U
409-
vertexDataF32[7] = r.y; // V
409+
vertexDataF32[7] = 1.0 - r.y; // V
410410

411411
// POS: w, 0, 0
412412
vertexDataF32[8] = w; // PX
413413
vertexDataF32[13] = 1; // NZ
414414
vertexDataF32[14] = r.x + r.z; // U
415-
vertexDataF32[15] = r.y; // V
415+
vertexDataF32[15] = 1.0 - r.y; // V
416416

417417
// POS: w, h, 0
418418
vertexDataF32[16] = w; // PX
419419
vertexDataF32[17] = h; // PY
420420
vertexDataF32[21] = 1; // NZ
421421
vertexDataF32[22] = r.x + r.z; // U
422-
vertexDataF32[23] = r.y + r.w; // V
422+
vertexDataF32[23] = 1.0 - (r.y + r.w); // V
423423

424424
// POS: 0, h, 0
425425
vertexDataF32[25] = h; // PY
426426
vertexDataF32[29] = 1; // NZ
427427
vertexDataF32[30] = r.x; // U
428-
vertexDataF32[31] = r.y + r.w; // V
428+
vertexDataF32[31] = 1.0 - (r.y + r.w); // V
429429

430430
var vertexDesc = [
431431
{ semantic: SEMANTIC_POSITION, components: 3, type: TYPE_FLOAT32 },
@@ -554,13 +554,13 @@ class ImageElement {
554554

555555
// Update vertex texture coordinates
556556
vertexDataF32[6] = rect.x / atlasTextureWidth;
557-
vertexDataF32[7] = rect.y / atlasTextureHeight;
557+
vertexDataF32[7] = 1.0 - rect.y / atlasTextureHeight;
558558
vertexDataF32[14] = (rect.x + rect.z) / atlasTextureWidth;
559-
vertexDataF32[15] = rect.y / atlasTextureHeight;
559+
vertexDataF32[15] = 1.0 - rect.y / atlasTextureHeight;
560560
vertexDataF32[22] = (rect.x + rect.z) / atlasTextureWidth;
561-
vertexDataF32[23] = (rect.y + rect.w) / atlasTextureHeight;
561+
vertexDataF32[23] = 1.0 - (rect.y + rect.w) / atlasTextureHeight;
562562
vertexDataF32[30] = rect.x / atlasTextureWidth;
563-
vertexDataF32[31] = (rect.y + rect.w) / atlasTextureHeight;
563+
vertexDataF32[31] = 1.0 - (rect.y + rect.w) / atlasTextureHeight;
564564

565565
vb.unlock();
566566

src/framework/components/element/text-element.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -943,16 +943,16 @@ class TextElement {
943943
var uv = this._getUv(char);
944944

945945
meshInfo.uvs[quad * 4 * 2 + 0] = uv[0];
946-
meshInfo.uvs[quad * 4 * 2 + 1] = uv[1];
946+
meshInfo.uvs[quad * 4 * 2 + 1] = 1.0 - uv[1];
947947

948948
meshInfo.uvs[quad * 4 * 2 + 2] = uv[2];
949-
meshInfo.uvs[quad * 4 * 2 + 3] = uv[1];
949+
meshInfo.uvs[quad * 4 * 2 + 3] = 1.0 - uv[1];
950950

951951
meshInfo.uvs[quad * 4 * 2 + 4] = uv[2];
952-
meshInfo.uvs[quad * 4 * 2 + 5] = uv[3];
952+
meshInfo.uvs[quad * 4 * 2 + 5] = 1.0 - uv[3];
953953

954954
meshInfo.uvs[quad * 4 * 2 + 6] = uv[0];
955-
meshInfo.uvs[quad * 4 * 2 + 7] = uv[3];
955+
meshInfo.uvs[quad * 4 * 2 + 7] = 1.0 - uv[3];
956956

957957
// set per-vertex color
958958
if (this._symbolColors) {

src/graphics/program-lib/chunks/particle.frag

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ float unpackFloat(vec4 rgbaDepth) {
2727
#endif
2828

2929
void main(void) {
30-
vec4 tex = texture2DSRGB(colorMap, texCoordsAlphaLife.xy);
30+
vec4 tex = texture2DSRGB(colorMap, vec2(texCoordsAlphaLife.x, 1.0 - texCoordsAlphaLife.y));
3131
vec4 ramp = texture2DSRGB(colorParam, vec2(texCoordsAlphaLife.w, 0.0));
3232
ramp.rgb *= colorMult;
3333

src/graphics/program-lib/chunks/particle_cpu.vert

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,11 @@ void main(void)
7474
vec2 velocityV = normalize((mat3(matrix_view) * inVel).xy); // should be removed by compiler if align/stretch is not used
7575

7676
vec2 quadXY = vertPos.xy;
77-
78-
#ifndef USE_MESH
79-
texCoordsAlphaLife = vec4(quadXY * -0.5 + 0.5, particle_vertexData2.z, particle_vertexData.w);
80-
#else
77+
78+
#ifdef USE_MESH
8179
texCoordsAlphaLife = vec4(particle_vertexData5.zw, particle_vertexData2.z, particle_vertexData.w);
80+
#else
81+
texCoordsAlphaLife = vec4(quadXY * -0.5 + 0.5, particle_vertexData2.z, particle_vertexData.w);
8282
#endif
8383
mat2 rotMatrix;
8484

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
vec3 normalMap = normalize(texture2D(normalMap, texCoordsAlphaLife.xy).xyz * 2.0 - 1.0);
1+
vec3 normalMap = normalize(texture2D(normalMap, vec2(texCoordsAlphaLife.x, 1.0 - texCoordsAlphaLife.y)).xyz * 2.0 - 1.0);
22
vec3 normal = ParticleMat * normalMap;

src/graphics/program-lib/chunks/reproject.frag

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,8 @@ vec3 fromSpherical(vec2 uv) {
119119
}
120120

121121
vec4 sampleEquirect(vec2 sph) {
122-
return texture2D(sourceTex, sph / vec2(PI * 2.0, PI) + 0.5);
122+
vec2 uv = sph / vec2(PI * 2.0, PI) + 0.5;
123+
return texture2D(sourceTex, vec2(uv.x, 1.0 - uv.y));
123124
}
124125

125126
vec4 sampleEquirect(vec3 dir) {
@@ -135,7 +136,7 @@ vec4 sampleCubemap(vec2 sph) {
135136
}
136137

137138
vec3 getDirectionEquirect() {
138-
return fromSpherical((vUv0 * 2.0 - 1.0) * vec2(PI, PI * 0.5));
139+
return fromSpherical((vec2(vUv0.x, 1.0 - vUv0.y) * 2.0 - 1.0) * vec2(PI, PI * 0.5));
139140
}
140141

141142
// octahedral code, based on http://jcgt.org/published/0003/02/01
@@ -159,7 +160,7 @@ vec3 octDecode(vec2 o) {
159160
}
160161

161162
vec3 getDirectionOctahedral() {
162-
return octDecode(vUv0 * 2.0 - 1.0);
163+
return octDecode(vec2(vUv0.x, 1.0 - vUv0.y) * 2.0 - 1.0);
163164
}
164165

165166
// Assumes that v is a unit vector. The result is an octahedral vector on the [-1, +1] square
@@ -173,7 +174,8 @@ vec2 octEncode(in vec3 v) {
173174
}
174175

175176
vec4 sampleOctahedral(vec3 dir) {
176-
return texture2D(sourceTex, octEncode(dir) * 0.5 + 0.5);
177+
vec2 uv = octEncode(dir) * 0.5 + 0.5;
178+
return texture2D(sourceTex, vec2(uv.x, 1.0 - uv.y));
177179
}
178180

179181
vec4 sampleOctahedral(vec2 sph) {
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
nineSlicedUv = vUv0;
2+
nineSlicedUv.y = 1.0 - nineSlicedUv.y;
3+

src/graphics/program-lib/chunks/startNineSlicedTiled.frag

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@
44
vec2 clampedUv = mix(innerOffset.xy * 0.5, vec2(1.0) - innerOffset.zw * 0.5, fract((vTiledUv - tileSize) * tileScale));
55
clampedUv = clampedUv * atlasRect.zw + atlasRect.xy;
66
nineSlicedUv = vUv0 * tileMask + clampedUv * (vec2(1.0) - tileMask);
7+
nineSlicedUv.y = 1.0 - nineSlicedUv.y;
8+

src/graphics/texture.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ class Texture {
128128
this._cubemap = false;
129129
this._volume = false;
130130
this.fixCubemapSeams = false;
131-
this._flipY = true;
131+
this._flipY = false;
132132
this._premultiplyAlpha = false;
133133

134134
this._isRenderTarget = false;

src/resources/cubemap.js

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ class CubemapHandler {
239239

240240
// one of the dependent assets has finished loading
241241
let awaiting = 7;
242-
const onReady = function (index, asset) {
242+
const onLoad = function (index, asset) {
243243
assets[index] = asset;
244244
awaiting--;
245245

@@ -250,28 +250,6 @@ class CubemapHandler {
250250
}
251251
};
252252

253-
// handle a texture asset finished loading.
254-
// the engine is unable to flip ImageBitmaps (to orient them correctly for cubemaps)
255-
// so we do that here.
256-
const onLoad = function (index, asset) {
257-
const level0 = asset && asset.resource && asset.resource._levels[0];
258-
if (level0 && typeof ImageBitmap !== 'undefined' && level0 instanceof ImageBitmap) {
259-
createImageBitmap(level0, {
260-
premultiplyAlpha: 'none',
261-
imageOrientation: 'flipY'
262-
})
263-
.then(function (imageBitmap) {
264-
asset.resource._levels[0] = imageBitmap;
265-
onReady(index, asset);
266-
})
267-
.catch(function (e) {
268-
callback(e);
269-
});
270-
} else {
271-
onReady(index, asset);
272-
}
273-
};
274-
275253
// handle an asset load failure
276254
const onError = function (index, err, asset) {
277255
callback(err);
@@ -299,10 +277,10 @@ class CubemapHandler {
299277

300278
if (!assetId) {
301279
// no asset
302-
onReady(i, null);
280+
onLoad(i, null);
303281
} else if (self.compareAssetIds(assetId, loadedAssetIds[i])) {
304282
// asset id hasn't changed from what is currently set
305-
onReady(i, loadedAssets[i]);
283+
onLoad(i, loadedAssets[i]);
306284
} else if (parseInt(assetId, 10) === assetId) {
307285
// assetId is an asset id
308286
texAsset = registry.get(assetId);

src/resources/parser/glb-parser.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1594,9 +1594,9 @@ const createResources = function (device, gltf, bufferViews, textureAssets, opti
15941594
}
15951595

15961596
// The original version of FACT generated incorrectly flipped V texture
1597-
// coordinates. We must compensate by -not- flipping V in this case. Once
1597+
// coordinates. We must compensate by flipping V in this case. Once
15981598
// all models have been re-exported we can remove this flag.
1599-
const disableFlipV = gltf.asset && gltf.asset.generator === 'PlayCanvas';
1599+
const disableFlipV = !(gltf.asset && gltf.asset.generator === 'PlayCanvas');
16001600

16011601
const nodes = createNodes(gltf, options);
16021602
const scenes = createScenes(gltf, nodes);

src/resources/parser/json-model.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ class JsonModelParser {
296296
iterator.element[attributeMap[attributeName]].set(attribute.data[j]);
297297
break;
298298
case 2:
299-
iterator.element[attributeMap[attributeName]].set(attribute.data[j * 2], attribute.data[j * 2 + 1]);
299+
iterator.element[attributeMap[attributeName]].set(attribute.data[j * 2], 1.0 - attribute.data[j * 2 + 1]);
300300
break;
301301
case 3:
302302
iterator.element[attributeMap[attributeName]].set(attribute.data[j * 3], attribute.data[j * 3 + 1], attribute.data[j * 3 + 2]);

src/resources/parser/texture/img.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ class ImgParser {
1818
// by default don't try cross-origin, because some browsers send different cookies (e.g. safari) if this is set.
1919
this.crossOrigin = registry.prefix ? 'anonymous' : null;
2020
this.maxRetries = 0;
21-
// disable ImageBitmap
21+
// As of today (9 Jul 2021) ImageBitmap only works on Chrome:
22+
// - Firefox doesn't support options parameter to createImageBitmap (see https://bugzilla.mozilla.org/show_bug.cgi?id=1533680)
23+
// - Safari supports ImageBitmap only as experimental feature.
2224
this.useImageBitmap = false && typeof ImageBitmap !== 'undefined' && /Firefox/.test(navigator.userAgent) === false;
2325
}
2426

@@ -105,8 +107,7 @@ class ImgParser {
105107
callback(err);
106108
} else {
107109
createImageBitmap(blob, {
108-
premultiplyAlpha: 'none',
109-
imageOrientation: 'flipY'
110+
premultiplyAlpha: 'none'
110111
})
111112
.then(function (imageBitmap) {
112113
callback(null, imageBitmap);

src/scene/immediate.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ class ImmediateData {
139139
void main(void) {
140140
gl_Position = matrix_model * vec4(aPosition, 0, 1);
141141
uv0 = aPosition.xy + 0.5;
142+
uv0.y = 1.0 - uv0.y;
142143
}
143144
`;
144145
}

0 commit comments

Comments
 (0)