I made an AFrame component for creating hotspot, and I want to change material.offset.y of a hotspot when I hover it with cursor. So I first tried using AFrame animaiton attribute, but visibly, we can’t access to this property with animation.
So I added a eventListener to the hotspot JS object and changing the getObject3D(‘mesh’).material.map.offset.y, but it update all hotspots texture when I hover one, and don’t know why… I checked if this was pointing on a specific hotspot, and it is ! So I don’t understand why all textures are updating…
Here is the code:
<a-scene light="defaultLightsEnabled: false"> <a-assets> <!-- Loading Scenes --> <img src="./assets/scene/scene.jpg" id="scene_spherical" /> <!-- Loading Icons --> <img src="./assets/icons/close.png" id="icon_close" /> <img src="./assets/icons/hotspot_sprite.png" id="icon_hotspot" /> <img src="./assets/icons/powered.png" id="icon_powered" /> <img src="./assets/icons/store.png" id="icon_store" /> <!-- Loading Mixins --> <a-mixin id="mixin_hotspot" geometry="primitive: circle; radius: 0.25" material="shader: flat; src: #icon_hotspot; transparent: true; repeat: 1 0.5; offset: 0 0.5;" ></a-mixin> </a-assets> <a-hotspot position="-6.33 0.30 -1.46" product="terrazzo_kaza_d" class="" id="hotspot_terrazzo_kaza_d"></a-hotspot> <a-hotspot position="5.43 -0.03 -6.21" product="meuble_tv" class="" id="hotspot_meuble_tv"></a-hotspot> <a-hotspot position="3.34 -0.81 -7.77" product="tapis_lake" class="" id="hotspot_tapis_lake"></a-hotspot> <a-hotspot position="5.30 1.22 -0.81" product="isole_escalier" class="" id="hotspot_isole_escalier"></a-hotspot> <a-hotspot position="-3.74 1.10 7.27" product="papier_peint" class="" id="hotspot_papier_peint"></a-hotspot> <a-hotspot position="3.09 -0.69 7.35" product="shooting_sol" class="" id="hotspot_shooting_sol"></a-hotspot> <a-hotspot position="-6.98 1.86 0.60" product="isole_cuisine" class="" id="hotspot_isole_cuisine"></a-hotspot> <a-hotspot position="-0.38 -0.32 5.98" product="isole_table_rectangulaire" class="" id="hotspot_isole_table_rectangulaire"></a-hotspot> <a-hotspot position="2.66 1.76 -8.15" product="isole_voilage" class="" id="hotspot_isole_voilage"></a-hotspot> <a-hotspot position="-1.13 -0.34 -7.41" product="isole_canape2" class="" id="hotspot_isole_canape2"></a-hotspot> <a-sky src="#scene_spherical"></a-sky> <a-camera wasd-controls="enabled: false;" cursor="rayOrigin: mouse"></a-camera> </a-scene>
import loadProduct from '../libs/shop' AFRAME.registerPrimitive('a-hotspot', { defaultComponents: { 'hotspot-popup': {} }, mappings: { product: 'hotspot-popup.product', } }) AFRAME.registerComponent('hotspot-popup', { schema: { // variables product: {type: 'string'} }, init: function() { this.el.setAttribute('mixin', 'mixin_hotspot') this.setHover() this.setClick() }, setHover() { this.el.addEventListener('mouseenter', () => { let material = this.el.getObject3D('mesh').material if (material.map) { material.map.offset.y = 0 material.map.offset.x = 0 } }) this.el.addEventListener('mouseleave', () => { let material = this.el.getObject3D('mesh').material if (material.map) { material.map.offset.y = 0.5 material.map.offset.x = 0 } }) }, tick: function () { let cursorRotation = document.querySelector('a-camera').getAttribute('rotation') this.el.setAttribute('rotation', cursorRotation) }, setClick: function () { this.el.addEventListener('click', () => { console.log('load', this.data.product) loadProduct(this.data.product) }); } })
So if anyone know how to prevent this behaviour, don’t hesitate to comment this post please.
Thanks, Navalex
Advertisement
Answer
The reason why all images are given the same offset is because a-frame
re-uses textures for performance reasons.
With a setup like this:
<a-box material="src: #image"></a-box> <a-sphere material="src: #image"></a-sphere >
If you log both element.getObject3D("mesh").material.map.uuid
– they will be the same (fiddle). Updating one affects the other.
If you give them an initial offset in the material
(but a different one for each object):
<a-box material="src: #image; offset: 0 0.5"></a-box> <a-sphere material="src: #image"></a-sphere >
a-frame
will create another instance of THREE.Texture()
them (fiddle).
Otherwise, you could create a component
which replaces the texture with a copy:
this.el.addEventListener("materialtextureloaded", e => { // grab the object let obj = this.el.getObject3D("mesh") // grab the texture let map = obj.material.map // create a new one const texture = new THREE.TextureLoader().load(map.image.src); // without wrapping, it will just "stretch" instead of "repeating" texture.wrapS = texture.wrapT = THREE.RepeatWrapping; // assign the new texture obj.material.map = texture; // update the material obj.material.needsUpdate = true })
Which brings us to something like this (including animation, wrapping etc)