Skip to content

Commit

Permalink
[FIX] Adopt top-left texture origin (#3335)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
slimbuck and willeastcott authored Jul 15, 2021
1 parent 03762bd commit ec4b67f
Show file tree
Hide file tree
Showing 23 changed files with 112 additions and 109 deletions.
Binary file removed examples/assets/textures/playcanvas.basis
Binary file not shown.
Binary file removed examples/assets/textures/sea.basis
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
2 changes: 1 addition & 1 deletion examples/src/examples/graphics/render-to-texture.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ class RenderToTextureExample extends Example {
// create a plane called tv which we use to display rendered texture
// this is only added to excluded Layer, so it does not render into texture
const tv = createPrimitive("plane", new pc.Vec3(6, 8, -5), new pc.Vec3(20, 10, 10), pc.Color.BLACK, [excludedLayer.id]);
tv.setLocalEulerAngles(90, 0, 0);
tv.setLocalEulerAngles(90, 0, 180);
tv.render.castShadows = false;
tv.render.receiveShadows = false;
// @ts-ignore engine-tsd
Expand Down
91 changes: 54 additions & 37 deletions examples/src/examples/graphics/texture-basis.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,21 @@ class TextureBasisExample extends Example {
static CATEGORY = 'Graphics';
static NAME = 'Texture Basis';

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

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

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

app.scene.ambientLight = new pc.Color(1, 1, 1);

// material using basis texture
const material1 = new pc.StandardMaterial();
material1.diffuseMap = assets.seaBasis.resource;
material1.update();

// Create a Entity with a Box render component
const box1 = new pc.Entity();
box1.addComponent("render", {
type: "box",
material: material1
// Set skybox
app.scene.gammaCorrection = pc.GAMMA_SRGB;
app.scene.toneMapping = pc.TONEMAP_ACES;
app.scene.skyboxMip = 1;
app.scene.skyboxIntensity = 0.7;
app.scene.setSkybox(assets.helipad.resources);

// Create directional light
const light = new pc.Entity();
light.addComponent('light', {
type: 'directional'
});

// another material using basis texture
const material2 = new pc.StandardMaterial();
material2.diffuseMap = assets.playcanvasBasis.resource;
material2.update();

const box2 = new pc.Entity();
box2.addComponent("render", {
type: "box",
material: material2
light.setLocalEulerAngles(45, 0, 45);

// Construct material
const material = new pc.StandardMaterial();
material.useMetalness = true;
material.diffuse = new pc.Color(0.3, 0.3, 0.3);
material.shininess = 80;
material.metalness = 0.7;
material.diffuseMap = assets.color.resource;
material.normalMap = assets.normal.resource;
material.glossMap = assets.gloss.resource;
material.diffuseMapTiling.set(7, 7);
material.normalMapTiling.set(7, 7);
material.glossMapTiling.set(7, 7);
material.update();

// Create a torus shape
const torus = pc.createTorus(app.graphicsDevice, {
tubeRadius: 0.2,
ringRadius: 0.3,
segments: 50,
sides: 40
});
const shape = new pc.Entity();
shape.addComponent('render', {
material: material,
meshInstances: [new pc.MeshInstance(torus, material)]
});
shape.setPosition(0, 0, 0);
shape.setLocalScale(2, 2, 2);

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

// Adjust the camera position
camera.translate(0, 0, 5);
camera.translate(0, 0, 4);

// Add the new Entities to the hierarchy
app.root.addChild(box1);
app.root.addChild(box2);
app.root.addChild(light);
app.root.addChild(shape);
app.root.addChild(camera);

box1.setPosition(0, -1, 0);
box2.setPosition(0, 1, 0);

// Set an update function on the app's update event
let angle = 0;
app.on("update", function (dt) {
angle += dt;
if (angle > 360) {
angle = 0;
}
angle = (angle + dt * 10) % 360;

// Rotate the boxes
box1.setEulerAngles(angle * 2, angle * 4, angle * 8);
box2.setEulerAngles(90 - angle * 12, 120 - angle * 8, 150 - angle * 10);
shape.setEulerAngles(angle, angle * 2, angle * 4);
});
}
}
Expand Down
16 changes: 8 additions & 8 deletions src/framework/components/element/image-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -406,26 +406,26 @@ class ImageElement {
// POS: 0, 0, 0
vertexDataF32[5] = 1; // NZ
vertexDataF32[6] = r.x; // U
vertexDataF32[7] = r.y; // V
vertexDataF32[7] = 1.0 - r.y; // V

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

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

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

var vertexDesc = [
{ semantic: SEMANTIC_POSITION, components: 3, type: TYPE_FLOAT32 },
Expand Down Expand Up @@ -554,13 +554,13 @@ class ImageElement {

// Update vertex texture coordinates
vertexDataF32[6] = rect.x / atlasTextureWidth;
vertexDataF32[7] = rect.y / atlasTextureHeight;
vertexDataF32[7] = 1.0 - rect.y / atlasTextureHeight;
vertexDataF32[14] = (rect.x + rect.z) / atlasTextureWidth;
vertexDataF32[15] = rect.y / atlasTextureHeight;
vertexDataF32[15] = 1.0 - rect.y / atlasTextureHeight;
vertexDataF32[22] = (rect.x + rect.z) / atlasTextureWidth;
vertexDataF32[23] = (rect.y + rect.w) / atlasTextureHeight;
vertexDataF32[23] = 1.0 - (rect.y + rect.w) / atlasTextureHeight;
vertexDataF32[30] = rect.x / atlasTextureWidth;
vertexDataF32[31] = (rect.y + rect.w) / atlasTextureHeight;
vertexDataF32[31] = 1.0 - (rect.y + rect.w) / atlasTextureHeight;

vb.unlock();

Expand Down
8 changes: 4 additions & 4 deletions src/framework/components/element/text-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -943,16 +943,16 @@ class TextElement {
var uv = this._getUv(char);

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

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

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

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

// set per-vertex color
if (this._symbolColors) {
Expand Down
2 changes: 1 addition & 1 deletion src/graphics/program-lib/chunks/particle.frag
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ float unpackFloat(vec4 rgbaDepth) {
#endif

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

Expand Down
8 changes: 4 additions & 4 deletions src/graphics/program-lib/chunks/particle_cpu.vert
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,11 @@ void main(void)
vec2 velocityV = normalize((mat3(matrix_view) * inVel).xy); // should be removed by compiler if align/stretch is not used

vec2 quadXY = vertPos.xy;

#ifndef USE_MESH
texCoordsAlphaLife = vec4(quadXY * -0.5 + 0.5, particle_vertexData2.z, particle_vertexData.w);
#else

#ifdef USE_MESH
texCoordsAlphaLife = vec4(particle_vertexData5.zw, particle_vertexData2.z, particle_vertexData.w);
#else
texCoordsAlphaLife = vec4(quadXY * -0.5 + 0.5, particle_vertexData2.z, particle_vertexData.w);
#endif
mat2 rotMatrix;

Expand Down
2 changes: 1 addition & 1 deletion src/graphics/program-lib/chunks/particle_normalMap.frag
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
vec3 normalMap = normalize(texture2D(normalMap, texCoordsAlphaLife.xy).xyz * 2.0 - 1.0);
vec3 normalMap = normalize(texture2D(normalMap, vec2(texCoordsAlphaLife.x, 1.0 - texCoordsAlphaLife.y)).xyz * 2.0 - 1.0);
vec3 normal = ParticleMat * normalMap;
10 changes: 6 additions & 4 deletions src/graphics/program-lib/chunks/reproject.frag
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ vec3 fromSpherical(vec2 uv) {
}

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

vec4 sampleEquirect(vec3 dir) {
Expand All @@ -135,7 +136,7 @@ vec4 sampleCubemap(vec2 sph) {
}

vec3 getDirectionEquirect() {
return fromSpherical((vUv0 * 2.0 - 1.0) * vec2(PI, PI * 0.5));
return fromSpherical((vec2(vUv0.x, 1.0 - vUv0.y) * 2.0 - 1.0) * vec2(PI, PI * 0.5));
}

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

vec3 getDirectionOctahedral() {
return octDecode(vUv0 * 2.0 - 1.0);
return octDecode(vec2(vUv0.x, 1.0 - vUv0.y) * 2.0 - 1.0);
}

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

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

vec4 sampleOctahedral(vec2 sph) {
Expand Down
2 changes: 2 additions & 0 deletions src/graphics/program-lib/chunks/startNineSliced.frag
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
nineSlicedUv = vUv0;
nineSlicedUv.y = 1.0 - nineSlicedUv.y;

2 changes: 2 additions & 0 deletions src/graphics/program-lib/chunks/startNineSlicedTiled.frag
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@
vec2 clampedUv = mix(innerOffset.xy * 0.5, vec2(1.0) - innerOffset.zw * 0.5, fract((vTiledUv - tileSize) * tileScale));
clampedUv = clampedUv * atlasRect.zw + atlasRect.xy;
nineSlicedUv = vUv0 * tileMask + clampedUv * (vec2(1.0) - tileMask);
nineSlicedUv.y = 1.0 - nineSlicedUv.y;

2 changes: 1 addition & 1 deletion src/graphics/texture.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ class Texture {
this._cubemap = false;
this._volume = false;
this.fixCubemapSeams = false;
this._flipY = true;
this._flipY = false;
this._premultiplyAlpha = false;

this._isRenderTarget = false;
Expand Down
28 changes: 3 additions & 25 deletions src/resources/cubemap.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ class CubemapHandler {

// one of the dependent assets has finished loading
let awaiting = 7;
const onReady = function (index, asset) {
const onLoad = function (index, asset) {
assets[index] = asset;
awaiting--;

Expand All @@ -250,28 +250,6 @@ class CubemapHandler {
}
};

// handle a texture asset finished loading.
// the engine is unable to flip ImageBitmaps (to orient them correctly for cubemaps)
// so we do that here.
const onLoad = function (index, asset) {
const level0 = asset && asset.resource && asset.resource._levels[0];
if (level0 && typeof ImageBitmap !== 'undefined' && level0 instanceof ImageBitmap) {
createImageBitmap(level0, {
premultiplyAlpha: 'none',
imageOrientation: 'flipY'
})
.then(function (imageBitmap) {
asset.resource._levels[0] = imageBitmap;
onReady(index, asset);
})
.catch(function (e) {
callback(e);
});
} else {
onReady(index, asset);
}
};

// handle an asset load failure
const onError = function (index, err, asset) {
callback(err);
Expand Down Expand Up @@ -299,10 +277,10 @@ class CubemapHandler {

if (!assetId) {
// no asset
onReady(i, null);
onLoad(i, null);
} else if (self.compareAssetIds(assetId, loadedAssetIds[i])) {
// asset id hasn't changed from what is currently set
onReady(i, loadedAssets[i]);
onLoad(i, loadedAssets[i]);
} else if (parseInt(assetId, 10) === assetId) {
// assetId is an asset id
texAsset = registry.get(assetId);
Expand Down
4 changes: 2 additions & 2 deletions src/resources/parser/glb-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -1594,9 +1594,9 @@ const createResources = function (device, gltf, bufferViews, textureAssets, opti
}

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

const nodes = createNodes(gltf, options);
const scenes = createScenes(gltf, nodes);
Expand Down
2 changes: 1 addition & 1 deletion src/resources/parser/json-model.js
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ class JsonModelParser {
iterator.element[attributeMap[attributeName]].set(attribute.data[j]);
break;
case 2:
iterator.element[attributeMap[attributeName]].set(attribute.data[j * 2], attribute.data[j * 2 + 1]);
iterator.element[attributeMap[attributeName]].set(attribute.data[j * 2], 1.0 - attribute.data[j * 2 + 1]);
break;
case 3:
iterator.element[attributeMap[attributeName]].set(attribute.data[j * 3], attribute.data[j * 3 + 1], attribute.data[j * 3 + 2]);
Expand Down
7 changes: 4 additions & 3 deletions src/resources/parser/texture/img.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ class ImgParser {
// by default don't try cross-origin, because some browsers send different cookies (e.g. safari) if this is set.
this.crossOrigin = registry.prefix ? 'anonymous' : null;
this.maxRetries = 0;
// disable ImageBitmap
// As of today (9 Jul 2021) ImageBitmap only works on Chrome:
// - Firefox doesn't support options parameter to createImageBitmap (see https://bugzilla.mozilla.org/show_bug.cgi?id=1533680)
// - Safari supports ImageBitmap only as experimental feature.
this.useImageBitmap = false && typeof ImageBitmap !== 'undefined' && /Firefox/.test(navigator.userAgent) === false;
}

Expand Down Expand Up @@ -105,8 +107,7 @@ class ImgParser {
callback(err);
} else {
createImageBitmap(blob, {
premultiplyAlpha: 'none',
imageOrientation: 'flipY'
premultiplyAlpha: 'none'
})
.then(function (imageBitmap) {
callback(null, imageBitmap);
Expand Down
1 change: 1 addition & 0 deletions src/scene/immediate.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ class ImmediateData {
void main(void) {
gl_Position = matrix_model * vec4(aPosition, 0, 1);
uv0 = aPosition.xy + 0.5;
uv0.y = 1.0 - uv0.y;
}
`;
}
Expand Down
Loading

0 comments on commit ec4b67f

Please sign in to comment.