I'm using a spritesheet (atlas) for low res textures, I want to able to repeat the same portion of the texture multipĺe times without adding more triangles.
I coded so far a plane like this:
var textureSpritemap = loadTexture('textures.png');
var geometry = new t3.PlaneGeometry(80, 80);
var material = new t3.MeshBasicMaterial({map: textureSpritemap});
var plane = new t3.Mesh(geometry, material);
setPlaneUVs(plane, [0, 0.5, 0, 0, 0.5, 0.5, 0.5, 0]);
textureSpritemap.repeat.set(2, 2);
I understand that it's possible to repeat a texture multiple times, but I want to be able to repeat only a portion.
Sprite map:
Intended result:
Actual result:
Any thoughts?
Related
I am currently trying to project an image onto the inside of a halfsphere in a three.js project. The half sphere is created via
const geometry = new THREE.SphereGeometry(Component.radius, this.resolution, this.resolution,
Math.PI, Math.PI, 0, Math.PI);
this.material = new THREE.MeshStandardMaterial({color: 0xffffff});
this.material.side = THREE.DoubleSide;
this.sphere = new THREE.Mesh(geometry, this.material);
// The image of the texture is set later dynamically via
// this.material.map = textureLoader.load(...);
With radius and resolution being constants. This works fine, except for one issue: The image becomes distorted around the "top" and "bottom" of the sphere, like this:
Simple example of distorted texture:
The texture originally had the value "0,0" in the bottom left and "0,1" in the bottom right, and with the camera facing down from the center of the demisphere the bottom left and bottom right corner are squished onto the "bottom" point of the sphere.
I want to change this behavior so the texture corners are instead on where they would be if you place the square texture into a sphere, with the corners touching the circle, then stretching the lines between the corners to match the circle. Simple mock of what i mean:
I have tried playing with the mapping Attribute of my texture, but that doesn't change the behaviour from my understanding.
After changing the UV coordinates, my half sphere texture is't stretching on border as well:
this.sphereGeometry = new THREE.SphereGeometry(
10,
32,
24,
0,
Math.PI,
0,
Math.PI
);
const {
uv: uvAttribute,
normal
} = this.sphereGeometry.attributes;
for (let i = 0; i < normal.count; i += 1) {
let u = normal.getX(i);
let v = normal.getY(i);
u = u / 2 + 0.5;
v = v / 2 + 0.5;
uvAttribute.setXY(i, u, v);
}
const texture = new THREE.TextureLoader().load(
'https://i.imgur.com/CslEXIS.jpg'
);
texture.flipY = false;
texture.mapping = THREE.CubeUVRefractionMapping;
texture.needsUpdate = true;
const basicMaterial = new THREE.MeshBasicMaterial({
map: texture,
side: THREE.DoubleSide,
});
this.sphere = new THREE.Mesh(this.sphereGeometry, basicMaterial);
this.scene.add(this.sphere);
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.
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.
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.
I'm currently loading an STL onject into my three.js scene.
For some reason, it takes a lot of GPU resources to render/animate, slowing the entire scene down so I've been considering alternatives.
As it's quite a simple shape, I thought I could use create the 2D shape and extrude it.
The 3D shape is a square frame (it's a photo frame), no curves or any other clever geometry.
Initially, I thought about creating 4x 3D oblongs, rotating each one by 90 degrees and placing them just right in the scene to make it look like a frame - but that's not ideal.
So as an alternative to loading the STL model into the scene, how can I create this shape in three.js (with empty space in the centre) and then extrude it to give it some depth?
Basic extrusion example: Shape -> ExtrudeGeometry -> Mesh
const { renderer, scene, camera } = initThree();
//Create a frame shape..
var frame = new THREE.Shape();
frame.moveTo(-4, -3);
frame.lineTo( 4, -3);
frame.lineTo( 4, 3);
frame.lineTo(-4, 3);
//..with a hole:
var hole = new THREE.Path();
hole.moveTo(-3, -2);
hole.lineTo( 3, -2);
hole.lineTo( 3, 2);
hole.lineTo(-3, 2);
frame.holes.push(hole);
//Extrude the shape into a geometry, and create a mesh from it:
var extrudeSettings = {
steps: 1,
depth: 1,
bevelEnabled: false,
};
var geom = new THREE.ExtrudeGeometry(frame, extrudeSettings);
var mesh = new THREE.Mesh(geom, new THREE.MeshPhongMaterial({ color: 0xffaaaa }));
scene.add(mesh);
renderer.render(scene, camera);
body {
margin: 0;
overflow: hidden;
}
canvas {
display: block;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/three.js/102/three.min.js"></script>
<script src="//cdn.rawgit.com/Sphinxxxx/298702f070e34a5df30326cd9943260a/raw/16afc701da1ed8ed267a896907692d8acdce9b7d/init-three.js"></script>
I'm using the babylonjs 3D WebGL library.
It's great library, but I can't find the same, which exists in THREE.JS library.
For example, I have 2D polygons in database, I'm fetching the polygon data from it and then create a custom mesh and extruding it.
With the THREE.JS, there isn't any problem, I can add to some array:
...
points.push( new THREE.Vector2( part.x, -part.y ) );
...
var shape = new THREE.Shape( points );
var extrusion = {
amount: building.height,
bevelEnabled: false
};
var geometry = new THREE.ExtrudeGeometry( shape, extrusion );
var mesh = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial({
ambient: 0xbbbbb,
color: 0xff0000
});
...
scene.add( mesh );
It's very simple. How to do the same, I couldn't find.
I've found only some information here:
http://www.html5gamedevs.com/topic/4530-create-a-mesh-from-a-list-of-vertices-and-faces/
http://blogs.msdn.com/b/eternalcoding/archive/2013/06/27/babylon-js-a-complete-javascript-framework-for-building-3d-games-with-html-5-and-webgl.aspx
With such an example (from msdn by Ctrl + F -> You can also create a mesh from a list of vertices and faces):
var plane = new BABYLON.Mesh(name, scene);
var indices = [];
var positions = [];
var normals = [];
var uvs = [];
// Vertices
var halfSize = size / 2.0;
positions.push(-halfSize, -halfSize, 0);
normals.push(0, 0, -1.0);
uvs.push(0.0, 0.0);
positions.push(halfSize, -halfSize, 0);
normals.push(0, 0, -1.0);
uvs.push(1.0, 0.0);
positions.push(halfSize, halfSize, 0);
normals.push(0, 0, -1.0);
uvs.push(1.0, 1.0);
positions.push(-halfSize, halfSize, 0);
normals.push(0, 0, -1.0);
uvs.push(0.0, 1.0);
// Indices
indices.push(0);
indices.push(1);
indices.push(2);
indices.push(0);
indices.push(2);
indices.push(3);
plane.setVerticesData(positions, BABYLON.VertexBuffer.PositionKind);
plane.setVerticesData(normals, BABYLON.VertexBuffer.NormalKind);
plane.setVerticesData(uvs, BABYLON.VertexBuffer.UVKind);
plane.setIndices(indices);
return plane;
But's it's rather not the same as with the THREE.JS. For example I need to count index buffer manually where in THREE.JS I don't need it, also it's a sample with plane only and I didn't find any info about extruding exactly.
So... Maybe, there are some easy ways in BabylonJS?
There is no support for extrusion right now but this could be a great feature to add :) I will definitely add it to our roadmap. If you would like to discuss the issue further please ask on the babylon.js forum.
EDIT:
Have ability to create custom shapes now.
http://doc.babylonjs.com/tutorials/parametric_shapes#extrusion
Update 2019 PolygonMeshBuilder allows to create custom mesh from the collection of vertexes
Please note that the PolygonMeshBuilder uses Earcut, so, in non
playground projects, you will have to add a reference to their cdn or
download their npm package
Add Earcut as dependency in your index HTML
<script src="https://preview.babylonjs.com/earcut.min.js"></script>
Now you can use Pramaetric shapes to extrude polygon and punch holes.
//Polygon shape in XoZ plane
var shape = [
new BABYLON.Vector3(4, 0, -4),
new BABYLON.Vector3(2, 0, 0),
new BABYLON.Vector3(5, 0, 2),
new BABYLON.Vector3(1, 0, 2),
new BABYLON.Vector3(-5, 0, 5),
new BABYLON.Vector3(-3, 0, 1),
new BABYLON.Vector3(-4, 0, -4),
new BABYLON.Vector3(-2, 0, -3),
new BABYLON.Vector3(2, 0, -3)
];
//Holes in XoZ plane
var holes = [];
holes[0] = [ new BABYLON.Vector3(1, 0, -1),
new BABYLON.Vector3(1.5, 0, 0),
new BABYLON.Vector3(1.4, 0, 1),
new BABYLON.Vector3(0.5, 0, 1.5)
];
holes[1] = [ new BABYLON.Vector3(0, 0, -2),
new BABYLON.Vector3(0.5, 0, -1),
new BABYLON.Vector3(0.4, 0, 0),
new BABYLON.Vector3(-1.5, 0, 0.5)
];
var polygon = BABYLON.MeshBuilder.ExtrudePolygon("polygon",{
shape:shape,
holes:holes,
depth: 2,
sideOrientation: BABYLON.Mesh.DOUBLESIDE }, scene);
Result:
See this playground for reference:
https://playground.babylonjs.com/#4G18GY#7
Advance usage
building Staircases:
https://www.babylonjs-playground.com/#RNCYVM#74
Is it possible to load multiple textures on a sphere?
I mean to say is there any way in Three.js to split a sphere into n pieces , texture them separately and render those pieces once again as a whole sphere?
I do not want to load the entire texture on the sphere, instead, only those parts are to be rendered which the user will first see on the screen and as the user rotates the sphere the rest part of the texture must be loaded.
Moreover, when I use a single image on a sphere it seems to converge at poles making it worse.
This should help: https://open.bekk.no/procedural-planet-in-webgl-and-three-js
Instead of using a sphere try using a cube and expanding it into a sphere. Cube logic on the cube sphere will save you a good amount of time.
var geometry = new THREE.BoxGeometry( 1, 1, 1, 8, 8, 8 );
for ( var i in geometry.vertices ) {
var vertex = geometry.vertices[ i ];
vertex.normalize().multiplyScalar(radius);
}
var materialArray = [];
var faceMaterial = new THREE.MeshLambertMaterial({
color: sphereColor,
transparent: true,
opacity: 0.4
});
for (var i = 0; i < 6; i++) {
materialArray.push(faceMaterial);
}
var material = new THREE.MeshFaceMaterial(materialArray);
var sphere = new THREE.Mesh( geometry, material );
scene.add( sphere );