Octahedrom Geometry with multiple texture images

Hi everyone.
I am trying to render an <octahedronGeometry args={[1.5, 0]} / > in react.js.
function Octahedron() {
const colorMap = useTexture([
“/geometry/alpine.jpg”,
“/geometry/ferrari.jpg”,
“/geometry/rb.jpg”,
“/geometry/mclaren.jpg”,
“/geometry/am.jpg”,
“/geometry/mercedes.jpg”,
“/geometry/am.jpg”,
“/geometry/mercedes.jpg”,
]);

return (

<octahedronGeometry args={[1.5, 0]} />
{colorMap.map((texture, idx) => (
<meshBasicMaterial
key={texture.id}
attach={material-${idx}}
map={texture}
/>
))}

);
}

this is my code. in this case the component is not rendering. If i comment the map function, the shape will render but empty. i tried multiple types of materials but none work.
If i use instead of the octahedron, the faces of the cube are filled with my images.
Can i have some support/ guidance on this?

Hi!
BoxGeometry has groups.
OctahedronGeometry doesn’t.

There are two possible approaches:

  1. Add groups to OctahedronGeometry.
  2. Modify geometry and material’s shaders, so you can pass a texture atlas in a uniform.

Here is the first approach, with groups and changed UVs: https://codepen.io/prisoner849/pen/wvjQBNp?editors=0010

Code:

function setOctahedron(g) {
  let pos = g.attributes.position;
  let faces = pos.count / 3;
  let groupStart = 0;
  for (let i = 0; i < faces; i++) {
    g.addGroup(groupStart, 3, i);
    groupStart += 3;
  }
  let uvs = [];
  uvs.push(0.5,1,0.06698729810778059,0.25,0.9330127018922194,0.25);
  uvs.push(0.06698729810778059,0.75,0.5,0,0.9330127018922194,0.75);
  uvs.push(0.5,0,0.9330127018922194,0.75,0.06698729810778059,0.75);
  uvs.push(0.9330127018922194,0.25,0.5,1,0.06698729810778059,0.25);
  uvs.push(0.5,1,0.06698729810778059,0.25,0.9330127018922194,0.25);
  uvs.push(0.06698729810778059,0.75,0.5,0,0.9330127018922194,0.75);
  uvs.push(0.5,0,0.9330127018922194,0.75,0.06698729810778059,0.75);
  uvs.push(0.9330127018922194,0.25,0.5,1,0.06698729810778059,0.25);
  g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2));
}
2 Likes

A dynamic variant (which also results in a static octahedron for appropriate parameters) can be found there:

Addon to create special / extended geometries

GitHub - hofk/THREEg.js: three.js addon to create special or extended geometries. The addon generates indexed or non indexed BufferGeometries.

Hi. Thanks for the replies, I appreciate your help. Do you know if there is any reliable support/ docs for React? I will try to adapt this code in React and see what I get.

function OctahedronComp({ geometryData }: ComponentProps) {
  const urlArray = geometryData.map((item) => imageBuilder(item.img).url());
  const colorMap = useTexture(urlArray);
  const uvs = new Float32Array([
    0.5, 1, 0.06698729810778059, 0.25, 0.9330127018922194, 0.25,
    0.06698729810778059, 0.75, 0.5, 0, 0.9330127018922194, 0.75, 0.5, 0,
    0.9330127018922194, 0.75, 0.06698729810778059, 0.75, 0.9330127018922194,
    0.25, 0.5, 1, 0.06698729810778059, 0.25, 0.5, 1, 0.06698729810778059, 0.25,
    0.9330127018922194, 0.25, 0.06698729810778059, 0.75, 0.5, 0,
    0.9330127018922194, 0.75, 0.5, 0, 0.9330127018922194, 0.75,
    0.06698729810778059, 0.75, 0.9330127018922194, 0.25, 0.5, 1,
    0.06698729810778059, 0.25,
  ]);

  const groups = new Float32Array([
    0, 3, 0, 3, 3, 1, 6, 3, 2, 9, 3, 3, 12, 3, 4, 15, 3, 5, 18, 3, 6, 21, 3, 7,
  ]);
  console.log(groups);

  return (
    <mesh>
      <octahedronBufferGeometry>
        <bufferAttribute
          attach="attributes-groups"
          array={groups}
          count={groups.length / 3}
          itemSize={3}
        />
        <bufferAttribute
          attach="attributes-uv"
          array={uvs}
          count={uvs.length / 6}
          itemSize={6}
        />
        {colorMap.map((texture, idx) => (
          <meshLambertMaterial
            key={texture.id}
            attach={`material-${idx}`}
            map={texture}
          />
        ))}
      </octahedronBufferGeometry>
    </mesh>
  );
}

currently rendering the octahedron but empty… how does attribute buffer know I am trying to .addGroup ? should the attach property contain a name in a different syntax?

I don’t work with React.

Here is another approach with building of an octahedron from scratch (see Octahedron class):

1 Like

Hi @prisoner849 . Thank you for the help. After all I chose to use the javascript approach and fill the octahedron with different images. The next thing that I would like to do, is to attach some functions/ functionality to each of the faces. When I inspect the page, I only see the canvas. And i am trying to console log the click event on it, I don’t see anything related to the faces.
Q1. How could I add a click event listener on a face?
Q2 How could I add hover functionality on a certain face?

Use Raycaster with pointer events to get faceIndex

Hi @prisoner849. Thanks again for you latest reply. I just came back to work again on this.
I played a bit with it and came down to last few questions.
My ultimate goal would be to obtain something similar to this https://polygonstudios.com/

  1. Initially I would like to have faces filled with some sort of textures and only on hover to show another textures. How is this change done? Using what you showed me above with Raycaster and modifying the textures array from the “face” object? or there is another cleaner way to make these kind of transitions?
  2. Looking at the images, the Field of view when the octahedron is rotated using the mouse is changing, if we look from one side, we can see more from the image when we look straight to it. ( where can i find some details about this functionality?
  3. The first texture colors are changing, is this done using some lights?
    Thank you!
  1. When raycaster detects a face, get its index and run a tweening, mixing textures from one to another. The same for when your mouse/pointer leaves the octahedron, run a tweening to return the previous texture.
  2. Looks like they change UV coords in dependence of the angle of sight on a face.
  3. Didn’t get this question :thinking:

Thanks for quick responses.

While i get the idea, not sure what tweening means, can you give me a short explanations if you don’t mind?
Also for question 3, I mean how do I obtain that kind of texture similar to theirs? If you don;t use the mouse, the colors are changing and seems like floating around the octahedron.
Thanks

@prisoner849 also if you can help me a bit with this bug. When the geometry is centered, the hover works correctly, but as soon as I start scrolling the points where the mouse “touches” the geometry are not accurate. I was thinking this could be an issue with the camera, but I am not sure
This is my current code.

let scene = new THREE.Scene();
    let camera = isMobile
      ? new THREE.PerspectiveCamera(60, (innerWidth * 3) / innerHeight, 1, 1000)
      : new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
    camera.position.set(0, 0, 10);
    let renderer = new THREE.WebGLRenderer();
    renderer.setSize(innerWidth, isMobile ? innerHeight * 0.4 : innerHeight);
    document.getElementById("id1").appendChild(renderer.domElement);
    let controls = new OrbitControls(camera, renderer.domElement);
    controls.autoRotate = true;
    controls.autoRotateSpeed = 5;
    controls.enableZoom = false;
    
    let g = isMobile
      ? new THREE.OctahedronGeometry(5)
      : new THREE.OctahedronGeometry(4);
    console.log(g);
    setOctahedron(g);
    let tl = new THREE.TextureLoader();
    // let m = geometryData.map((item) => {
    //   return new THREE.MeshLambertMaterial({
    //     map: tl.load(imageBuilder(item.img).url()),
    //   });
    // });
    var m = [
      new THREE.MeshPhongMaterial({
        color: 0x00ff00,
      }),
      new THREE.MeshPhongMaterial({
        color: 0x00ff00,
      }),
      new THREE.MeshPhongMaterial({
        color: 0x00ff00,
      }),
      new THREE.MeshPhongMaterial({
        color: 0x00ff00,
      }),
      new THREE.MeshPhongMaterial({
        color: 0x00ff00,
      }),
      new THREE.MeshPhongMaterial({
        color: 0x00ff00,
      }),
      new THREE.MeshPhongMaterial({
        color: 0x00ff00,
      }),
      new THREE.MeshPhongMaterial({
        color: 0x00ff00,
      }),
    ];
    let o = new THREE.Mesh(g, m);
    scene.add(o);
    console.log(scene);
    scene.background = new THREE.Color(0xffffff);
    let raycaster = new THREE.Raycaster();
    let pointer = new THREE.Vector2();
    let intersected = null;
    let activeFaceIndex = 0;
    renderer.domElement.addEventListener(
      "mousemove",
      (event) => {
        pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
        pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;

        raycaster.setFromCamera(pointer, camera);
        intersected = raycaster.intersectObject(o, false);
        if (intersected.length > 0) {
          console.log(intersected);
          if (
            intersected[0].face.materialIndex !== activeFaceIndex &&
            activeFaceIndex !== -1
          ) {
            m[activeFaceIndex].color.setHex(0x00ff00);
          }
          activeFaceIndex = intersected[0].face.materialIndex;
          m[activeFaceIndex].color.setHex(0xff0000);

          // let fId = intersected[0].faceIndex;
          // console.log(intersected[0]);
          // intersected[0].object.material[fId].map = tl.load(
          //   imageBuilder(geometryData[0].img).url()
          // );

          // console.log("Face #" + fId + " clicked");
        } else {
          if (activeFaceIndex !== -1) {
            m[activeFaceIndex].color.setHex(0x00ff00);
          }
          activeFaceIndex = -1;
        }
      },
      false
    );
    renderer.setAnimationLoop(() => {
      controls.update();
      renderer.render(scene, camera);
    });

Some info: Inbetweening - Wikipedia
And an example: How to animate the texture map change of MeshBasicMaterial using gsap ? (or any other possible method) - #6 by prisoner849
If you can’t see it on jsfiddle, there is a good collection of examples from the forum, gathered by @hofk: AnimateTextureMap

Still the same, like in 2. They change colors in dependence of the angle of view.

Have a look at this forum post from @looeee: [SOLVED] Raycaster on mouse click laggs an action behind - #2 by looeee

1 Like

anything that’s possible in three is possible in react, if you put your code into a codesandbox i can fix it up. but when in doubt you can use vanilla code 1:1 and just do <primitive object={…

but basically having multiple materials is just this

<mesh>
  <boxGeometry />
  <meshNormalMaterial attach="material-0" />
  <meshBasicMaterial color="hotpink" attach="material-1" />
  <meshBasicMaterial color="blue" attach="material-2" />
  <meshBasicMaterial color="red" attach="material-3" />
  <meshBasicMaterial color="orange" attach="material-4" />
  <meshBasicMaterial color="goldenrod" attach="material-5" />
</mesh>

though you’d get such things resolved faster on pmndrs discord. here im probably the only one that answers three+react.