Change floor image in canvas 3D

I am trying to create a 3D room in which user can change the tiles and visualize the changes.

I want to change floor’s tile when user clicks on the floor, how to achieve it.

Added Image URLs for the transparent Room as well (wall without tiles).

Note : Bundle.js is compiled version of js file created to run project without re-run using rollup npm.

floor tile image url : https://res.cloudinary.com/ecommerceapi/image/upload/v1712137072/LivingRoom/FloorTiles/1678707660-Twilight-Grey.jpg

My HTML

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Three JS</title>
</head>

<body>
  <div id="container"></div>
  <script src="bundle.js"></script>
</body>

</html>

js code

import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";

let camera, controls;
let renderer;
let scene;

init();
animate();

function init() {
  const container = document.getElementById("container");
  renderer = new THREE.WebGLRenderer();
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setClearColor(0xffffff, 1);

  container.appendChild(renderer.domElement);

  scene = new THREE.Scene();

  camera = new THREE.PerspectiveCamera(
    90,
    window.innerWidth / window.innerHeight,
    0.1,
    100
  );
  camera.position.z = 0.01;

  controls = new OrbitControls(camera, renderer.domElement);
  controls.enableZoom = false;
  controls.enablePan = false;
  controls.enableDamping = true;
  controls.rotateSpeed = -0.25;

  const textures = [];
  const urls = [
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711983804/LivingRoom/MATT/matt_left.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711983804/LivingRoom/MATT/matt_right.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711983804/LivingRoom/MATT/matt_top.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711983804/LivingRoom/MATT/matt_bottom.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711983804/LivingRoom/MATT/matt_back.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711983804/LivingRoom/MATT/matt_front.webp",
  ];

  const transparentImageUrls = [
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711982339/LivingRoom/Transparent/cutt_left.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711982324/LivingRoom/Transparent/cutt_right.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711982343/LivingRoom/Transparent/cutt_top.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711982354/LivingRoom/Transparent/cutt_bottom.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711982360/LivingRoom/Transparent/cutt_back.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711982363/LivingRoom/Transparent/cutt_front.webp",
  ];


  const textureLoader = new THREE.TextureLoader();
  for (let i = 0; i < 6; i++) {
    const texture = textureLoader.load(urls[i]);
    // const texture = textureLoader.load(transparentImageUrls [i]);
    texture.colorSpace = THREE.SRGBColorSpace; // Ensure textures are loaded in sRGB color space
    textures.push(texture);
  }

  const materials = [];
  for (let i = 0; i < 6; i++) {
    materials.push(new THREE.MeshBasicMaterial({ map: textures[i] }));
  }

  const skyBox = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), materials);
  skyBox.geometry.scale(1, 1, -1);
  scene.add(skyBox);
  
  document.addEventListener('click', function(event) {
    console.log("called", event)
  });
  window.addEventListener("resize", onWindowResize);
}

function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
}

function animate() {
  requestAnimationFrame(animate);
  controls.update(); // required when damping is enabled
  renderer.render(scene, camera);
}

This example may be what you’re looking for? Raycaster is pretty much all you need.

Yes, I am trying with the reference you have provided, but it didn’t worked yet, and in my case needs to add the tile image for the respective position and only in transparent area of the main base image.

can you please guide further how it can be implemented in code ?

Your code does not have raycaster in it - pls share what you have and which part specifically is blocking you. It should be as easy as casting a ray, filtering intersections to include only the objects you’re looking for, and swapping .map prop on their material.

2 Likes

sorry for the delayed response. here is the updated code with raycaster. still not able to set the tile image correctly

attaching output as well.

import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";

let camera, controls;
let renderer;
let scene;
let raycaster;
let informazione = {
  floor_tile_width: 1500, // Provide the width of the floor tile
  floor_tile_height: 1500, // Provide the height of the floor tile
};
init();
animate();

function init() {
  const container = document.getElementById("container");
  renderer = new THREE.WebGLRenderer();
  raycaster = new THREE.Raycaster();
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  // renderer.outputColorSpace  = THREE.SRGBColorSpace; // Use outputEncoding instead of outputColorSpace
  renderer.setClearColor(0xffffff, 1);

  container.appendChild(renderer.domElement);

  scene = new THREE.Scene();

  camera = new THREE.PerspectiveCamera(
    90,
    window.innerWidth / window.innerHeight,
    0.1,
    100
  );
  camera.position.z = 0.01;

  controls = new OrbitControls(camera, renderer.domElement);
  controls.enableZoom = false;
  controls.enablePan = false;
  controls.enableDamping = true;
  controls.rotateSpeed = -0.25;

  const textures = [];
  const urls = [
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711983804/LivingRoom/MATT/matt_left.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711983804/LivingRoom/MATT/matt_right.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711983804/LivingRoom/MATT/matt_top.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711983804/LivingRoom/MATT/matt_bottom.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711983804/LivingRoom/MATT/matt_back.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711983804/LivingRoom/MATT/matt_front.webp",
  ];

  const textureLoader = new THREE.TextureLoader();
  for (let i = 0; i < 6; i++) {
    const texture = textureLoader.load(urls[i]);
    // texture.encoding = THREE.SRGBColorSpace; // Ensure textures are loaded in sRGB color space
    texture.colorSpace = THREE.SRGBColorSpace;
    textures.push(texture);
  }

  const materials = [];
  for (let i = 0; i < 6; i++) {
    materials.push(new THREE.MeshBasicMaterial({ map: textures[i] }));
  }

  const skyBox = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), materials);
  skyBox.geometry.scale(1, 1, -1);
  scene.add(skyBox);

  window.addEventListener("resize", onWindowResize);
}

function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
}

function animate() {
  requestAnimationFrame(animate);
  controls.update(); // required when damping is enabled
  renderer.render(scene, camera);
}

// Function to handle mouse click events
// Function to handle mouse click events
const floorGeometry = new THREE.PlaneGeometry(
  informazione.floor_tile_width,
  informazione.floor_tile_height
);
const floorMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff }); // Initial material without texture
const floorMesh = new THREE.Mesh(floorGeometry, floorMaterial);
floorMesh.name = "Floor"; // Assigning a name to the floor mesh
floorMesh.rotation.x = -Math.PI / 2; // Rotate the floor to be horizontal
scene.add(floorMesh);

function onMouseClick(event) {
  // Calculate mouse position in normalized device coordinates
  const mouse = new THREE.Vector2();
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

  // Set the raycaster to cast from the camera to the mouse position
  raycaster.setFromCamera(mouse, camera);

  // Find intersecting objects
  const intersects = raycaster.intersectObjects(scene.children, true);
  console.log(intersects);
  // If there are intersections, handle them
  if (intersects.length > 0) {
    // Check if the intersection is with the floor
    const floorIntersect = intersects.find(
      (intersection) => intersection.object.name === "Floor"
    );
    if (floorIntersect) {
      // Set the floor tile image
      const textureLoader = new THREE.TextureLoader();
      const floorTexture = textureLoader.load(
        "https://res.cloudinary.com/ecommerceapi/image/upload/v1712137072/LivingRoom/FloorTiles/1678707660-Twilight-Grey.jpg"
      );
      floorTexture.wrapS = THREE.RepeatWrapping;
      floorTexture.wrapT = THREE.RepeatWrapping;
      // Adjust the repeat value according to your tile size
      floorTexture.repeat.set(4, 4);
      // Apply the texture to the floor
      floorIntersect.object.material.map = floorTexture;
      floorIntersect.object.material.needsUpdate = true;
      console.log("Floor tile set");
    }
  }
}

document.addEventListener("click", onMouseClick, false);

can anyone guide, I am still facing issue

Firstly you want to position the floor mesh so that it matches the floor in your panorama.
In this case -0.5, although that will cause zfighting with your panorama cube, so try floor.position.y = -0.499
Your floor in the example code is 1.5 km x 1.5 km. Try reducing size.

import * as THREE from "three";
import { OrbitControls } from "addons/controls/OrbitControls.js";

let camera, controls;
let renderer;
let scene;
let raycaster;
let informazione = {
  floor_tile_width: 1.5, // Provide the width of the floor tile
  floor_tile_height: 1.5, // Provide the height of the floor tile
};
init();
animate();

function init() {
  const container = document.getElementById("container");
  renderer = new THREE.WebGLRenderer();
  raycaster = new THREE.Raycaster();
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  // renderer.outputColorSpace  = THREE.SRGBColorSpace; // Use outputEncoding instead of outputColorSpace
  renderer.setClearColor(0xffffff, 1);

  container.appendChild(renderer.domElement);

  scene = new THREE.Scene();

  camera = new THREE.PerspectiveCamera(
    90,
    window.innerWidth / window.innerHeight,
    0.1,
    100
  );
  camera.position.z = 0.01;

  controls = new OrbitControls(camera, renderer.domElement);
  controls.enableZoom = false;
  controls.enablePan = false;
  controls.enableDamping = true;
  controls.rotateSpeed = -0.25;

  const textures = [];
  const urls = [
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711983804/LivingRoom/MATT/matt_left.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711983804/LivingRoom/MATT/matt_right.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711983804/LivingRoom/MATT/matt_top.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711983804/LivingRoom/MATT/matt_bottom.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711983804/LivingRoom/MATT/matt_back.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711983804/LivingRoom/MATT/matt_front.webp",
  ];

  const textureLoader = new THREE.TextureLoader();
  for (let i = 0; i < 6; i++) {
    const texture = textureLoader.load(urls[i]);
    // texture.encoding = THREE.SRGBColorSpace; // Ensure textures are loaded in sRGB color space
    texture.colorSpace = THREE.SRGBColorSpace;
    textures.push(texture);
  }

  const materials = [];
  for (let i = 0; i < 6; i++) {
    materials.push(new THREE.MeshBasicMaterial({ map: textures[i] }));
  }

  const skyBox = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), materials);
  skyBox.geometry.scale(10, 10, -10);
  scene.add(skyBox);

  window.addEventListener("resize", onWindowResize);
}

function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
}

function animate() {
  requestAnimationFrame(animate);
  controls.update(); // required when damping is enabled
  renderer.render(scene, camera);
}

// Function to handle mouse click events
// Function to handle mouse click events
const floorGeometry = new THREE.PlaneGeometry(
  informazione.floor_tile_width,
  informazione.floor_tile_height
);
const floorMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff }); // Initial material without texture
const floorMesh = new THREE.Mesh(floorGeometry, floorMaterial);
floorMesh.name = "Floor"; // Assigning a name to the floor mesh
floorMesh.position.y = -0.49; // Set the floor below the camera
floorMesh.rotation.x = -Math.PI / 2; // Rotate the floor to be horizontal
scene.add(floorMesh);

function onMouseClick(event) {
  // Calculate mouse position in normalized device coordinates
  const mouse = new THREE.Vector2();
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

  // Set the raycaster to cast from the camera to the mouse position
  raycaster.setFromCamera(mouse, camera);

  // Find intersecting objects
  const intersects = raycaster.intersectObjects(scene.children, true);
  console.log(intersects);
  // If there are intersections, handle them
  if (intersects.length > 0) {
    // Check if the intersection is with the floor
    const floorIntersect = intersects.find(
      (intersection) => intersection.object.name === "Floor"
    );
    if (floorIntersect) {
      // Set the floor tile image
      const textureLoader = new THREE.TextureLoader();
      const floorTexture = textureLoader.load(
        "https://res.cloudinary.com/ecommerceapi/image/upload/v1712137072/LivingRoom/FloorTiles/1678707660-Twilight-Grey.jpg"
      );
      floorTexture.colorSpace = THREE.SRGBColorSpace;
      floorTexture.wrapS = THREE.RepeatWrapping;
      floorTexture.wrapT = THREE.RepeatWrapping;
      // Adjust the repeat value according to your tile size
      floorTexture.repeat.set(4, 4);
      // Apply the texture to the floor
      floorIntersect.object.material.map = floorTexture;
      floorIntersect.object.material.needsUpdate = true;
      console.log("Floor tile set");
    }
  }
}

document.addEventListener("click", onMouseClick, false);

Now you can change the behaviour when the floor is clicked on.
Perhaps display a range of floor materials to choose from?

1 Like

Thanks for the response, I want to set the floor as it should look a like actual floor tile, I have received some data for the x,y,z positions although sharing it here.

var informazione = {
  floor_tile_w: 6,
  floor_tile_h: 12,
  wall_tile_w: 12,
  wall_tile_h: 6,
  camera_fov: 75,
  camera_position_x: 0,
  camera_position_y: 0,
  camera_position_z: 3,
  intensity: 0.98,
  R_intensity: 0.01,
  wall_tile_width: 2400,
  wall_tile_height: 1200,
  floor_tile_width: 2400,
  floor_tile_height: 1200,
};
var data = [
  {
    name: "Wall 1",
    face: "wall",
    width: 285,
    height: 285,
    "position-x": 2,
    "position-y": 102,
    "position-z": 120,
    rotateX: (Math.PI / 180) * 180,
    color: 0xff0000,
    "rotation-y": Math.PI,
  },
  {
    name: "Wall 2",
    face: "wall",
    width: 285,
    height: 285,
    "position-x": 107,
    "position-y": 102,
    "position-z": 170,
    rotateX: (Math.PI / 180) * 180,
    color: 0x00ff00,
    "rotation-y": -Math.PI / 2,
  },
  {
    name: "Wall 3",
    face: "wall",
    width: 285,
    height: 285,
    "position-x": -79,
    "position-y": 102,
    "position-z": -101,
    rotateX: (Math.PI / 180) * 180,
    color: 0xfff000,
    "rotation-y": 0,
  },
  {
    name: "Floor",
    face: "floor",
    width: 1500,
    height: 1500,
    "position-x": 24,
    "position-y": -101,
    "position-z": 0,
    "rotation-x": Math.PI / 2,
  },
];
var angolo = [
  {
    name: "Corner 1",
    type: "corner",
    width: 0.3,
    height: 106.5,
    depth: 0.3,
    "position-x": 102,
    "position-y": 15,
    "position-z": 114,
    color: 0x333333,
    "rotation-y": Math.PI / 4,
  },
];

Yes, currently trying with a fixed wall & floor image to setup. later on based on the selection of tile, material should be applied
I am trying to setup according this. I would be truly grateful if you could help me a bit more with this.

attaching expected output


  1. Remember that your units are meters, no millimeters.
  2. Remove the floor from you panorama images, but retain the shadows as semi transparent.
  3. Place the dynamic floor BEHIND the panorama (floor.position.y = -0.501)

I am afraid I don’t have the time to provide an example of this.

1 Like

can you please share any reference for that ?

I will try to implement as per your suggestion, thanks again :slight_smile:

To be honest, I’ve not seen anyone do it this way before. It’s a novel approach, and I think it can work.
If you have the 3D environment in (for example) Blender, I would render out the images again, replacing the ground plane with a so called “shadow catcher” that is otherwise transparent.
If you only have the images, you’re stuck with editing them in Photoshop.

shall we try by creating a common gltf file for this. and apply texture on it for each side ? and after apply the tiles on floor or wall based on the selection ?

can we apply texture inside the 3D model ? as we have to keep camera position inside on the room.

please share any reference if available.

I hope this short video demonstrates @Peter_Devine suggestion. The left is one of the images, where I manually replaced the floor with just the shadow by using GIMP. Having this done, it is easy to put any tile below that shadow.

2 Likes

@PavelBoytchev @Peter_Devine

Thanks for the reply.
I have tried to implement the thing as suggested. please have a look. I have used transparent images without walls & floor as suggested. now I am trying to apply the tile on floor when user clicks on it. but still not getting properly placed. Floor overlaps the bottom objects

import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";

let camera, controls;
let renderer;
let scene;
let raycaster;
let informazione = {
  floor_tile_width: 1.5, // Provide the width of the floor tile
  floor_tile_height: 1.5, // Provide the height of the floor tile
};
init();
animate();

function init() {
  const container = document.getElementById("container");
  renderer = new THREE.WebGLRenderer();
  raycaster = new THREE.Raycaster();
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  // renderer.outputColorSpace  = THREE.SRGBColorSpace; // Use outputEncoding instead of outputColorSpace
  renderer.setClearColor(0xffffff, 1);

  container.appendChild(renderer.domElement);

  scene = new THREE.Scene();

  camera = new THREE.PerspectiveCamera(
    90,
    window.innerWidth / window.innerHeight,
    0.1,
    100
  );
  camera.position.z = 0.01;

  controls = new OrbitControls(camera, renderer.domElement);
  controls.enableZoom = false;
  controls.enablePan = false;
  controls.enableDamping = true;
  controls.rotateSpeed = -0.25;

  const textures = [];

  const transparentImageUrls = [
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711982339/LivingRoom/Transparent/cutt_left.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711982324/LivingRoom/Transparent/cutt_right.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711982343/LivingRoom/Transparent/cutt_top.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711982354/LivingRoom/Transparent/cutt_bottom.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711982360/LivingRoom/Transparent/cutt_back.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711982363/LivingRoom/Transparent/cutt_front.webp",
  ];

  const textureLoader = new THREE.TextureLoader();
  for (let i = 0; i < 6; i++) {
    const texture = textureLoader.load(transparentImageUrls[i]);
    // texture.encoding = THREE.SRGBColorSpace; // Ensure textures are loaded in sRGB color space
    texture.colorSpace = THREE.SRGBColorSpace;
    textures.push(texture);
  }

  const materials = [];
  for (let i = 0; i < 6; i++) {
    materials.push(new THREE.MeshBasicMaterial({ map: textures[i] }));
  }

  const skyBox = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), materials);
  skyBox.geometry.scale(10, 10, -10);
  scene.add(skyBox);

  window.addEventListener("resize", onWindowResize);
}

function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
}

function animate() {
  requestAnimationFrame(animate);
  controls.update(); // required when damping is enabled
  renderer.render(scene, camera);
}

// Function to handle mouse click events
// Function to handle mouse click events
const floorGeometry = new THREE.PlaneGeometry(
  informazione.floor_tile_width,
  informazione.floor_tile_height
);
const floorMaterial = new THREE.MeshBasicMaterial({
  color: 0xffffff,
  transparent: true,
  opacity: 0, // Initially invisible
});
const floorMesh = new THREE.Mesh(floorGeometry, floorMaterial);
floorMesh.name = "Floor"; // Assigning a name to the floor mesh
floorMesh.position.y = -0.501; // Set the floor below the camera
floorMesh.rotation.x = -Math.PI / 2; // Rotate the floor to be horizontal
scene.add(floorMesh);

function onMouseClick(event) {
  // Calculate mouse position in normalized device coordinates
  const mouse = new THREE.Vector2();
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

  // Set the raycaster to cast from the camera to the mouse position
  raycaster.setFromCamera(mouse, camera);

  // Find intersecting objects
  const intersects = raycaster.intersectObjects(scene.children, true);
  console.log(intersects);
  // If there are intersections, handle them
  if (intersects.length > 0) {
    // Check if the intersection is with the floor
    const floorIntersect = intersects.find(
      (intersection) => intersection.object.name === "Floor"
    );
    if (floorIntersect) {
      // Set the floor tile image
      const textureLoader = new THREE.TextureLoader();
      const floorTexture = textureLoader.load(
        "https://res.cloudinary.com/ecommerceapi/image/upload/v1712137072/LivingRoom/FloorTiles/1678707660-Twilight-Grey.jpg"
      );
      floorTexture.colorSpace = THREE.SRGBColorSpace;
      floorTexture.wrapS = THREE.RepeatWrapping;
      floorTexture.wrapT = THREE.RepeatWrapping;
      // Adjust the repeat value according to your tile size
      floorTexture.repeat.set(4, 4);
      // Apply the texture to the floor
      floorIntersect.object.material.map = floorTexture;
      floorIntersect.object.material.opacity = 0.5; // Adjust opacity as needed
      floorIntersect.object.material.transparent = true; // Set material to transparent
      floorIntersect.object.material.needsUpdate = true;

      //   scene.add(floorIntersect)
      console.log("Floor tile set");
    }
  }
}

document.addEventListener("click", onMouseClick, false);

Set your skybox scale back to 1,1,-1
Then make your skybox materials transparent.

  const materials = [];
  for (let i = 0; i < 6; i++) {
    materials.push(new THREE.MeshBasicMaterial({ map: textures[i], transparent: true}));
  }

  const skyBox = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), materials);
  skyBox.geometry.scale(1, 1, -1);
  scene.add(skyBox);

Works better than it should! :smile:

I didn’t realise you were doing walls as well. You will obviously need to add geometry for those, or if you do have the 3D file, export as a glb.

1 Like

@Peter_Devine Thanks, your suggested approach worked correctly. I have implemented those changes and trying to apply texture on walls as well. I am not to set proper coordinates. please guide how can we set the texture on walls. 3D model or glb file is not available. I am trying to replicate this.

current JS code

import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { informazione, data } from "./data.js";

let camera, controls;
let renderer;
let scene;
let raycaster;
init();
animate();

function init() {
  const container = document.getElementById("container");
  renderer = new THREE.WebGLRenderer();
  raycaster = new THREE.Raycaster();
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setClearColor(0xffffff, 1);

  container.appendChild(renderer.domElement);

  scene = new THREE.Scene();

  camera = new THREE.PerspectiveCamera(
    90,
    window.innerWidth / window.innerHeight,
    0.1,
    100
  );
  camera.position.z = 0.01;

  controls = new OrbitControls(camera, renderer.domElement);
  controls.enableZoom = false;
  controls.enablePan = false;
  controls.enableDamping = true;
  controls.rotateSpeed = -0.25;

  const textures = [];
  const transparentImageUrls = [
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711982339/LivingRoom/Transparent/cutt_left.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711982324/LivingRoom/Transparent/cutt_right.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711982343/LivingRoom/Transparent/cutt_top.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711982354/LivingRoom/Transparent/cutt_bottom.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711982360/LivingRoom/Transparent/cutt_back.webp",
    "https://res.cloudinary.com/ecommerceapi/image/upload/v1711982363/LivingRoom/Transparent/cutt_front.webp",
  ];

  const textureLoader = new THREE.TextureLoader();
  for (let i = 0; i < 6; i++) {
    const texture = textureLoader.load(transparentImageUrls[i]);
    texture.colorSpace = THREE.SRGBColorSpace;
    textures.push(texture);
  }

  const materials = [];
  for (let i = 0; i < 6; i++) {
    materials.push(
      new THREE.MeshBasicMaterial({ map: textures[i], transparent: true })
    );
  }
  const skyBox = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), materials);
  skyBox.geometry.scale(1, 1, -1);
  scene.add(skyBox);

  window.addEventListener("resize", onWindowResize);
}

function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
}

function animate() {
  requestAnimationFrame(animate);
  controls.update(); // required when damping is enabled
  renderer.render(scene, camera);
}

const floorGeometry = new THREE.PlaneGeometry(
  informazione.floor_tile_width,
  informazione.floor_tile_height
);
const floorMaterial = new THREE.MeshBasicMaterial({
  color: 0xffffff,
  transparent: true,
  opacity: 0, // Initially invisible
});
const floorMesh = new THREE.Mesh(floorGeometry, floorMaterial);
floorMesh.name = "Floor"; // Assigning a name to the floor mesh
floorMesh.position.y = -1; // Set the floor below the camera
floorMesh.rotation.x = -Math.PI / 2; // Rotate the floor to be horizontal
scene.add(floorMesh);

const wall1Geometry = new THREE.PlaneGeometry(
  informazione.wall_tile_width,
  informazione.wall_tile_height
);
const wall1Material = new THREE.MeshBasicMaterial({
  color: 0xffffff,
  transparent: true,
  opacity: 0, // Initially invisible
});
const wall1Mesh = new THREE.Mesh(wall1Geometry, wall1Material);
wall1Mesh.name = "Wall1"; // Assigning a name to the mesh
wall1Mesh.position.set(1, -1, 1);
wall1Mesh.rotation.x = -(Math.PI / 180) * 180; // Rotate the wall
scene.add(wall1Mesh);


const wall2Geometry = new THREE.PlaneGeometry(
  informazione.wall_tile_width,
  informazione.wall_tile_height
);
const wall2Material = new THREE.MeshBasicMaterial({
  color: 0xffffff,
  transparent: true,
  opacity: 0, // Initially invisible
});
const wall2Mesh = new THREE.Mesh(wall2Geometry, wall2Material);
wall2Mesh.name = "Wall2"; // Assigning a name to the mesh
wall2Mesh.position.set(75, 0, 75);
// wall2Mesh.rotation.y = -Math.PI; // Rotate the wall
scene.add(wall2Mesh);


const wall3Geometry = new THREE.PlaneGeometry(
  informazione.wall_tile_width,
  informazione.wall_tile_height
);
const wall3Material = new THREE.MeshBasicMaterial({
  color: 0xffffff,
  transparent: true,
  opacity: 0, // Initially invisible
});
const wall3Mesh = new THREE.Mesh(wall3Geometry, wall3Material);
wall3Mesh.name = "Wall3"; // Assigning a name to the mesh
wall3Mesh.position.set(2, 5, 10);
wall3Mesh.rotation.y = -Math.PI; // Rotate the wall
scene.add(wall3Mesh);


function onMouseClick(event) {
  // Calculate mouse position in normalized device coordinates
  const mouse = new THREE.Vector2();
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
  const textureLoader = new THREE.TextureLoader();

  // Set the raycaster to cast from the camera to the mouse position
  raycaster.setFromCamera(mouse, camera);

  // Find intersecting objects
  const intersects = raycaster.intersectObjects(scene.children, true);

  // If there are intersections, handle them
  if (intersects.length > 0) {
    // Check if the intersection is with the floor
    const floorIntersect = intersects.find(
      (intersection) => intersection.object.name === "Floor"
    );
    if (floorIntersect) {
      // Set the floor tile image
      const floorTexture = textureLoader.load(
        "https://res.cloudinary.com/ecommerceapi/image/upload/v1712137072/LivingRoom/FloorTiles/1678707660-Twilight-Grey.jpg"
      );
      floorTexture.colorSpace = THREE.SRGBColorSpace;
      floorTexture.wrapS = THREE.RepeatWrapping;
      floorTexture.wrapT = THREE.RepeatWrapping;
      // Adjust the repeat value according to your tile size
      floorTexture.repeat.set(4, 4);
      // Apply the texture to the floor
      floorIntersect.object.material.map = floorTexture;
      floorIntersect.object.material.opacity = 1; // Adjust opacity as needed
      floorIntersect.object.material.transparent = true; // Set material to transparent
      floorIntersect.object.material.needsUpdate = true;
      console.log("Floor tile set");
    }

    const wallIntersect = intersects.find(
      (intersection) => intersection.object.name === "Wall1"
    );
    if (wallIntersect) {
      // Set the wall tile image
      const wallTexture = textureLoader.load(
        "https://res.cloudinary.com/ecommerceapi/image/upload/v1713678490/LivingRoom/FloorTiles/2.webp"
      );
      wallTexture.colorSpace = THREE.SRGBColorSpace;
      wallTexture.wrapS = THREE.RepeatWrapping;
      wallTexture.wrapT = THREE.RepeatWrapping;
      // Adjust the repeat value according to your tile size
      wallTexture.repeat.set(8, 8);
      // Apply the texture to the floor
      wallIntersect.object.material.map = wallTexture;
      wallIntersect.object.material.opacity = 1; // Adjust opacity as needed
      wallIntersect.object.material.transparent = true; // Set material to transparent
      wallIntersect.object.material.needsUpdate = true;
      console.log("Wall 1 tile set");
    }


    const wall2Intersect = intersects.find(
      (intersection) => intersection.object.name === "Wall2"
    );
    if (wall2Intersect) {
      // Set the wall tile image
      const wallTexture = textureLoader.load(
        "https://res.cloudinary.com/ecommerceapi/image/upload/v1713678490/LivingRoom/FloorTiles/1.webp"
      );
      wallTexture.colorSpace = THREE.SRGBColorSpace;
      wallTexture.wrapS = THREE.RepeatWrapping;
      wallTexture.wrapT = THREE.RepeatWrapping;
      // Adjust the repeat value according to your tile size
      wallTexture.repeat.set(8, 8);
      // Apply the texture to the floor
      wall2Intersect.object.material.map = wallTexture;
      wall2Intersect.object.material.opacity = 1; // Adjust opacity as needed
      wall2Intersect.object.material.transparent = true; // Set material to transparent
      wall2Intersect.object.material.needsUpdate = true;
      console.log("Wall 2 tile set");
    }

    const wall3Intersect = intersects.find(
      (intersection) => intersection.object.name === "Wall3"
    );
    if (wall3Intersect) {
      // Set the wall tile image
      const wallTexture = textureLoader.load(
        "https://res.cloudinary.com/ecommerceapi/image/upload/v1713678490/LivingRoom/FloorTiles/3.webp"
      );
      wallTexture.colorSpace = THREE.SRGBColorSpace;
      wallTexture.wrapS = THREE.RepeatWrapping;
      wallTexture.wrapT = THREE.RepeatWrapping;
      // Adjust the repeat value according to your tile size
      wallTexture.repeat.set(8, 8);
      // Apply the texture to the floor
      wall3Intersect.object.material.map = wallTexture;
      wall3Intersect.object.material.opacity = 1; // Adjust opacity as needed
      wall3Intersect.object.material.transparent = true; // Set material to transparent
      wall3Intersect.object.material.needsUpdate = true;
      console.log("Wall 3 tile set");
    }
  }
}

document.addEventListener("click", onMouseClick, false);


data,js file :

var informazione = {
  floor_tile_w: 6,
  floor_tile_h: 12,
  wall_tile_w: 12,
  wall_tile_h: 6,
  camera_fov: 75,
  camera_position_x: 0,
  camera_position_y: 0,
  camera_position_z: 3,
  intensity: 0.98,
  R_intensity: 0.01,
  // wall_tile_width: 2400,
  // wall_tile_height: 1200,
  wall_tile_width:6,
  wall_tile_height:6,
  // floor_tile_width: 2400, //actual data
  // floor_tile_height: 1200, //actual data
  floor_tile_width: 8,
  floor_tile_height: 12,
};
var data = [
  {
    name: "Wall 1",
    face: "wall",
    width: 285,
    height: 285,
    "position-x": 2,
    "position-y": 102,
    "position-z": 120,
    rotateX: (Math.PI / 180) * 180,
    color: 0xff0000,
    "rotation-y": Math.PI,
  },
  {
    name: "Wall 2",
    face: "wall",
    width: 285,
    height: 285,
    "position-x": 107,
    "position-y": 102,
    "position-z": 170,
    rotateX: (Math.PI / 180) * 180,
    color: 0x00ff00,
    "rotation-y": -Math.PI / 2,
  },
  {
    name: "Wall 3",
    face: "wall",
    width: 285,
    height: 285,
    "position-x": -79,
    "position-y": 102,
    "position-z": -101,
    rotateX: (Math.PI / 180) * 180,
    color: 0xfff000,
    "rotation-y": 0,
  },
  {
    name: "Floor",
    face: "floor",
    width: 1500,
    height: 1500,
    "position-x": 24,
    "position-y": -101,
    "position-z": 0,
    "rotation-x": Math.PI / 2,
  },
];
var angolo = [
  {
    name: "Corner 1",
    type: "corner",
    width: 0.3,
    height: 106.5,
    depth: 0.3,
    "position-x": 102,
    "position-y": 15,
    "position-z": 114,
    color: 0x333333,
    "rotation-y": Math.PI / 4,
  },
];


export {data, informazione, angolo};


when I tried to apply texture on left wall. it overlaps the floor texture.
here is the codepen link of current code : https://codepen.io/Vatsal-Pandya-the-solid/pen/dyLQayZ
@PavelBoytchev @Peter_Devine @mjurczyk can you please guide how can we set the texture on the walls as the reference link. I’m new to ThreeJS and trying to understand the coordinates. Sorry for asking so many questions.

Without the source 3D file, you’re pretty much screwed.
I didn’t realise we were trying to rip off someone else’s solution…

If you build a 3D environment then you will have the wall geometry to apply your textures to.
Good Luck!

2 Likes

okay I understood without 3d object it’s hard to implement, can you please tell if the source website has used 3D model for that ? how can we check that from output ?

They will have. If you are going to make a business out of this, you will need to purchase some 3d environments, or build some. Get started with Blender today! :slight_smile:

2 Likes

okay thanks!

I am doing this currently for learning purpose only. I will try to start learning blender as soon as possible.

Thanks again for the help & guidance :slightly_smiling_face:

1 Like