Skip to content

Commit

Permalink
Unify texture loading and map property fixes (#5453)
Browse files Browse the repository at this point in the history
* Use updateMapMaterialFromData in updateDistortionMap

* Allow the various maps on standard and phong to be unset

* Flag texture as needsUpdate when texture properties change

---------

Co-authored-by: Noeri Huisman <mrxz@users.noreply.github.com>
  • Loading branch information
mrxz and mrxz authored Feb 9, 2024
1 parent 7ead8e6 commit 580d94a
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 58 deletions.
1 change: 0 additions & 1 deletion src/shaders/flat.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ module.exports.Shader = registerShader('flat', {
*/
init: function (data) {
this.materialData = {color: new THREE.Color()};
this.textureSrc = null;
getMaterialData(data, this.materialData);
this.material = new THREE.MeshBasicMaterial(this.materialData);
},
Expand Down
10 changes: 4 additions & 6 deletions src/shaders/phong.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,17 @@ module.exports.Shader = registerShader('phong', {
*/
init: function (data) {
this.materialData = { color: new THREE.Color(), specular: new THREE.Color(), emissive: new THREE.Color() };
this.textureSrc = null;
getMaterialData(data, this.materialData);
this.material = new THREE.MeshPhongMaterial(this.materialData);
utils.material.updateMap(this, data);
},

update: function (data) {
this.updateMaterial(data);
utils.material.updateMap(this, data);
if (data.normalMap) { utils.material.updateDistortionMap('normal', this, data); }
if (data.displacementMap) { utils.material.updateDistortionMap('displacement', this, data); }
if (data.ambientOcclusionMap) { utils.material.updateDistortionMap('ambientOcclusion', this, data); }
if (data.bump) { utils.material.updateDistortionMap('bump', this, data); }
utils.material.updateDistortionMap('normal', this, data);
utils.material.updateDistortionMap('displacement', this, data);
utils.material.updateDistortionMap('ambientOcclusion', this, data);
utils.material.updateDistortionMap('bump', this, data);
this.updateEnvMap(data);
},

Expand Down
10 changes: 5 additions & 5 deletions src/shaders/standard.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,11 @@ module.exports.Shader = registerShader('standard', {
update: function (data) {
this.updateMaterial(data);
utils.material.updateMap(this, data);
if (data.normalMap) { utils.material.updateDistortionMap('normal', this, data); }
if (data.displacementMap) { utils.material.updateDistortionMap('displacement', this, data); }
if (data.ambientOcclusionMap) { utils.material.updateDistortionMap('ambientOcclusion', this, data); }
if (data.metalnessMap) { utils.material.updateDistortionMap('metalness', this, data); }
if (data.roughnessMap) { utils.material.updateDistortionMap('roughness', this, data); }
utils.material.updateDistortionMap('normal', this, data);
utils.material.updateDistortionMap('displacement', this, data);
utils.material.updateDistortionMap('ambientOcclusion', this, data);
utils.material.updateDistortionMap('metalness', this, data);
utils.material.updateDistortionMap('roughness', this, data);
this.updateEnvMap(data);
},

Expand Down
74 changes: 28 additions & 46 deletions src/utils/material.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,40 @@ function setTextureProperties (texture, data) {
var offset = data.offset || {x: 0, y: 0};
var repeat = data.repeat || {x: 1, y: 1};
var npot = data.npot || false;
var anisotropy = data.anisotropy || 0;
var anisotropy = data.anisotropy || THREE.Texture.DEFAULT_ANISOTROPY;
var wrapS = texture.wrapS;
var wrapT = texture.wrapT;
var magFilter = texture.magFilter;
var minFilter = texture.minFilter;

// To support NPOT textures, wrap must be ClampToEdge (not Repeat),
// and filters must not use mipmaps (i.e. Nearest or Linear).
if (npot) {
texture.wrapS = THREE.ClampToEdgeWrapping;
texture.wrapT = THREE.ClampToEdgeWrapping;
texture.magFilter = THREE.LinearFilter;
texture.minFilter = THREE.LinearFilter;
wrapS = THREE.ClampToEdgeWrapping;
wrapT = THREE.ClampToEdgeWrapping;
magFilter = THREE.LinearFilter;
minFilter = THREE.LinearFilter;
}

// Don't bother setting repeat if it is 1/1. Power-of-two is required to repeat.
// Set wrap mode to repeat only if repeat isn't 1/1. Power-of-two is required to repeat.
if (repeat.x !== 1 || repeat.y !== 1) {
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(repeat.x, repeat.y);
}
// Don't bother setting offset if it is 0/0.
if (offset.x !== 0 || offset.y !== 0) {
texture.offset.set(offset.x, offset.y);
wrapS = THREE.RepeatWrapping;
wrapT = THREE.RepeatWrapping;
}

// Only set anisotropy if it isn't 0, which indicates that the default value should be used.
if (anisotropy !== 0) {
// Apply texture properties
texture.offset.set(offset.x, offset.y);
texture.repeat.set(repeat.x, repeat.y);

if (texture.wrapS !== wrapS || texture.wrapT !== wrapT ||
texture.magFilter !== magFilter || texture.minFilter !== minFilter ||
texture.anisotropy !== anisotropy) {
texture.wrapS = wrapS;
texture.wrapT = wrapT;
texture.magFilter = magFilter;
texture.minFilter = minFilter;
texture.anisotropy = anisotropy;
texture.needsUpdate = true;
}
}
module.exports.setTextureProperties = setTextureProperties;
Expand Down Expand Up @@ -127,43 +137,15 @@ module.exports.updateMap = function (shader, data) {
module.exports.updateDistortionMap = function (longType, shader, data) {
var shortType = longType;
if (longType === 'ambientOcclusion') { shortType = 'ao'; }
var el = shader.el;
var material = shader.material;
var rendererSystem = el.sceneEl.systems.renderer;
var src = data[longType + 'Map'];

var info = {};
info.src = src;
info.src = data[longType + 'Map'];

// Pass through the repeat and offset to be handled by the material loader.
info.offset = data[longType + 'TextureOffset'];
info.repeat = data[longType + 'TextureRepeat'];
info.wrap = data[longType + 'TextureWrap'];

if (src) {
if (src === shader[longType + 'TextureSrc']) { return; }

// Texture added or changed.
shader[longType + 'TextureSrc'] = src;
el.sceneEl.systems.material.loadTexture(src, info, setMap);
return;
}

// Texture removed.
if (!material.map) { return; }
setMap(null);

function setMap (texture) {
var slot = shortType + 'Map';
material[slot] = texture;
if (texture && COLOR_MAPS.has(slot)) {
rendererSystem.applyColorCorrection(texture);
}
if (texture) {
setTextureProperties(texture, data);
}
material.needsUpdate = true;
handleTextureEvents(el, texture);
}
return module.exports.updateMapMaterialFromData(shortType + 'Map', 'src', shader, info);
};

/**
Expand Down
15 changes: 15 additions & 0 deletions tests/components/material.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,21 @@ suite('material', function () {
});
});

test('updates texture when repeat changes', function (done) {
var imageUrl = 'base/tests/assets/test.png';
el.setAttribute('material', '');
assert.notOk(el.components.material.material.texture);
el.setAttribute('material', 'src: url(' + imageUrl + ')');
el.addEventListener('materialtextureloaded', function (evt) {
var loadedTexture = evt.detail.texture;
assert.ok(el.components.material.material.map === loadedTexture);
var version = loadedTexture.version;
el.setAttribute('material', 'repeat', '2 2');
assert.ok(el.components.material.material.map.version > version);
done();
});
});

test('removes texture when src attribute removed', function (done) {
var imageUrl = 'base/tests/assets/test.png';
el.setAttribute('material', '');
Expand Down
21 changes: 21 additions & 0 deletions tests/shaders/standard.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,27 @@ suite('standard material', function () {
});
});

[
{ dataName: 'normalMap', materialName: 'normalMap' },
{ dataName: 'displacementMap', materialName: 'displacementMap' },
{ dataName: 'ambientOcclusionMap', materialName: 'aoMap' },
{ dataName: 'metalnessMap', materialName: 'metalnessMap' },
{ dataName: 'roughnessMap', materialName: 'roughnessMap' }
].forEach(function (names) {
test(`can unset ${names.dataName}`, function (done) {
var el = this.el;
var imageUrl = 'base/tests/assets/test.png';
assert.isNull(el.getObject3D('mesh').material[names.materialName]);
el.setAttribute('material', names.dataName, `url(${imageUrl})`);
el.addEventListener('materialtextureloaded', function (evt) {
assert.equal(el.getObject3D('mesh').material[names.materialName], evt.detail.texture);
el.setAttribute('material', names.dataName, '');
assert.isNull(el.getObject3D('mesh').material[names.materialName]);
done();
});
});
});

test('can use spherical env maps', function (done) {
var el = this.el;
var imageUrl = 'base/tests/assets/test.png';
Expand Down

0 comments on commit 580d94a

Please sign in to comment.