DEV Community

Ali Ozzaim
Ali Ozzaim

Posted on

Creating a 3D Gallery App with Three.js, HTML, and CSS - Part 3

In the previous part of this series, we organized our code into modules and added four walls to our 3D gallery. Now, we will build on that foundation by adding a roof, floor, and textures, as well as some advanced features to make our gallery more interactive and visually appealing.

Updated Project Structure

First, let's add a few more files to our project structure for the textures:

3d-gallery-app/
├── index.html
├── styles.css
├── src/
│   ├── main.js
│   ├── modules/
│   │   ├── lighting.js
│   │   ├── walls.js
│   │   ├── roof.js
│   │   ├── floor.js
│   │   ├── textures.js
│   │   └── animation.js
├── public/
│   ├── models/
│   ├── textures/
│   │   ├── wall-texture.jpg
│   │   ├── floor-texture.jpg
│   │   └── roof-texture.jpg
├── package.json
├── vite.config.js

Enter fullscreen mode Exit fullscreen mode

Adding the Roof and Floor

Let's start by creating the roof and floor modules.

// src/modules/roof.js
import * as THREE from 'three';

export function addRoof(scene, textureLoader) {
    const roofTexture = textureLoader.load('public/textures/roof-texture.jpg');
    const roofMaterial = new THREE.MeshBasicMaterial({ map: roofTexture, side: THREE.DoubleSide });
    const roofGeometry = new THREE.PlaneGeometry(10, 10);
    const roof = new THREE.Mesh(roofGeometry, roofMaterial);

    roof.rotation.x = Math.PI / 2;
    roof.position.y = 5;
    scene.add(roof);
}

Enter fullscreen mode Exit fullscreen mode

floor.js

// src/modules/floor.js
import * as THREE from 'three';

export function addFloor(scene, textureLoader) {
    const floorTexture = textureLoader.load('public/textures/floor-texture.jpg');
    const floorMaterial = new THREE.MeshBasicMaterial({ map: floorTexture, side: THREE.DoubleSide });
    const floorGeometry = new THREE.PlaneGeometry(10, 10);
    const floor = new THREE.Mesh(floorGeometry, floorMaterial);

    floor.rotation.x = -Math.PI / 2;
    floor.position.y = -5;
    scene.add(floor);
}
Enter fullscreen mode Exit fullscreen mode

Updating main.js

Finally, update main.js to pass the texture loader to the modules when adding walls, roof, and floor.

import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { addLighting } from './modules/lighting.js';
import { addWalls } from './modules/walls.js';
import { addRoof } from './modules/roof.js';
import { addFloor } from './modules/floor.js';
import { animate } from './modules/animation.js';

// Scene, Camera, Renderer
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.getElementById('gallery').appendChild(renderer.domElement);

// Lighting
addLighting(scene);

// Texture Loader
const textureLoader = new THREE.TextureLoader();

// Load Models if you have one 
const loader = new THREE.GLTFLoader();
loader.load('assets/models/model.gltf', function(gltf) {
    scene.add(gltf.scene);
}, undefined, function(error) {
    console.error(error);
});

// Camera Position
camera.position.set(0, 2, 10);

// Add Walls, Roof, and Floor
addWalls(scene, textureLoader);
addRoof(scene, textureLoader);
addFloor(scene, textureLoader);

// Add OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.25;
controls.enableZoom = true;

// Animation Loop
animate(renderer, scene, camera, controls);

// Handle Window Resize
window.addEventListener('resize', () => {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
});

// Handle Mouse Click
function onDocumentMouseClick(event) {
    event.preventDefault();
    const mouse = new THREE.Vector2(
        (event.clientX / window.innerWidth) * 2 - 1,
        -(event.clientY / window.innerHeight) * 2 + 1
    );

    const raycaster = new THREE.Raycaster();
    raycaster.setFromCamera(mouse, camera);

    const intersects = raycaster.intersectObjects(scene.children, true);
    if (intersects.length > 0) {
        const intersected = intersects[0].object;
        console.log('Model clicked:', intersected.name);
        // Display model information
    }
}

document.addEventListener('click', onDocumentMouseClick, false);

Enter fullscreen mode Exit fullscreen mode

End of Part 3

By following these steps, you've successfully added a roof and floor to your 3D gallery and applied textures to make it more visually appealing.

Note

We haven't added new textures for the walls. You can search for wall textures online, download them, and replace the existing texture to further customize your gallery. Try to experiment with different textures to see how they change the look and feel of your gallery.

Continue to read for Part 4, which will cover user controls and key listeners for even more interactive features!

www.aliozzaim.com

Top comments (0)