NikDaOverlord
NikDaOverlord

Reputation: 37

How do I detect if X and Z position is intersecting a certain mesh (not using mouse)? - Three.js

Background of Question

I am working on a game that is a mix between Europa Universalis 4 and Age of Empires 3. The game is made in JavaScript and utilizes Three.js (r109) library. As of right now I have made randomly generated low-poly terrain with trees and reflective water. In the beginning I want the game to spawn a Navy, represented by a galleon (in screenshot below). I want to make it so when its called to spawn, it will pick a random location within the bounds of the water. The water mesh is represented by a semi-opaque plane spanning the size of the map- with a THREE.Reflector object underneath it. The terrain is also a plane but has been altered using a SimplexNoise heightmap.

enter image description here

The Question

How do I detect if an x and z position intersects with the water mesh and not the terrain mesh? THREE.Raycaster seems to be useful for what I am trying to do, but I wan't to know if there is a better solution. If using THREE.Raycaster is the best option, how would I go about implementing it for this purpose? Should I make an individual THREE.Raycaster for every object I am doing this with? Keep in mind I'm not placing this object with the mouse, I want to place it with a method that checks the position as stated above.

Upvotes: 0

Views: 319

Answers (1)

jbg
jbg

Reputation: 218

It's difficult to give specific advice without knowing anything at all about your code, but it sounds like all you need to do is create a collision list for your valid water surfaces and then check that when you want to spawn something.

A very simple jsfiddle is here. It creates a "land" mesh (green) and a "water" mesh (blue), adds the "water" mesh to a variable called collisionList. It then calls a spawn function for coordinates diagonally across both surfaces. The function uses a raycaster to check if the coordinates are over the "water" mesh and spawns a red cube if it is.

Here's the code:

window.onload = function() {
    var camera = null, land = null, water = null, renderer = null, lights;
    var collisionList;
    var d, n, scene = null, animID;

    n = document.getElementById('canvas');

    function load() {
            var height = 600, width = 800;

            scene = new THREE.Scene();
            camera = new THREE.PerspectiveCamera(60, width/height, 1, 1000);
            camera.position.set(0, 0, -10);

            camera.lookAt(new THREE.Vector3(0, 0, 0));
            scene.add(camera);

            lights = [];
            lights[0] = new THREE.PointLight(0xffffff, 1, 0);
            lights[1] = new THREE.PointLight(0xffffff, 1, 0);
            lights[2] = new THREE.PointLight(0xffffff, 1, 0);
            lights[0].position.set(0, 200, 0);
            lights[1].position.set(100, 200, 100);
            lights[2].position.set(-100, -200, -100);
            scene.add(lights[0]);
            scene.add(lights[1]);
            scene.add(lights[2]);

            water = new THREE.Mesh(new THREE.PlaneGeometry(7, 7, 10),
                    new THREE.MeshStandardMaterial({
                            color:  0x0000ff,
                            side:   THREE.DoubleSide,
                    }));
            water.position.set(0, 0, 0);
            scene.add(water);

            land = new THREE.Mesh(new THREE.PlaneGeometry(12, 12, 10),
                    new THREE.MeshStandardMaterial({
                            color:  0x00ff00,
                            side:   THREE.DoubleSide,
                    }));
            land.position.set(0, 0, 1);
            scene.add(land);

            renderer = new THREE.WebGLRenderer();
            renderer.setSize(width, height);
            n.appendChild(renderer.domElement);

            collisionList = [ water ];

            for(var i = -6; i < 6; i++)
                    spawn(i);
            
            animate();
    }
    function spawn(x) {
            var dir, intersect, mesh, ray, v;

            v = new THREE.Vector3(x, x, -1);
            dir = new THREE.Vector3(0, 0, 1);
            ray = new THREE.Raycaster(v, dir.normalize(), 0, 100);
            intersect = ray.intersectObjects(collisionList);
            if(intersect.length <= 0)
                    return;
            mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1, 1, 1, 1),
                    new THREE.MeshStandardMaterial({ color: 0xff0000 }));
            mesh.position.set(x, x, 0);
            scene.add(mesh);
    }
    function animate() {
            if(!scene) return;
            animID = requestAnimationFrame(animate);
            render();
            update();
    }
    function render() {
            if(!scene || !camera || !renderer) return;
            renderer.render(scene, camera);
    }
    function update() {
            if(!scene || !camera) return;
    }
    
    load();

As for whether this is a smart way to do it, that really depends on the design of the rest of your game.

If your world is procgen then it may be more efficient/less error prone to generate the spawn points (and any other "functional" parts of the world) first and use that to generate the geography instead of the other way around.

Upvotes: 1

Related Questions