Gutentag, guys!
I am trying to make a hole in my ExtrudeGeometry but it does not work.
My code looks like this:
var shape = new THREE.Mesh(new THREE.ExtrudeGeometry(drawShape(), options), new THREE.MeshLambertMaterial({color: 0x593788}))
function drawShape() {
// create a basic shape
var shape = new THREE.Shape();
shape.moveTo(10, 10);
shape.lineTo(10, 40);
shape.lineTo(30, 40);
shape.lineTo(30, 10);
shape.lineTo(10, 10);
var hole1 = new THREE.Path();
hole1.moveTo(12,15);
hole1.lineTo(15,16);
hole1.lineTo(16,16);
hole1.lineTo(16,15);
hole1.lineTo(12,15);
shape.holes.push(hole1);
return shape;
}
What I am getting is a plain shape figure with no hole
(drew a white hole with Clipping Tool to show where it should be) --->
May I ask why is this happening?
Because when I add absellipse or absarc - the hole works fine.
var hole2 = new THREE.Path();
hole2.absellipse(23, 24, 2, 3, 0, Math.PI * 2, true);
shape.holes.push(hole2);
Is it impossible to push holes made from line points instead of ellipse or arc?
If you have any idea why this does not work or how to fix it, please help.
Related
I want to introduce several three.js 3D models within different reveal.js slides. So far I achieved it partially. In each slide I introduce:
<section>
<script src="three_r84/model1.js"></script>
<div id="model1" width="950" height="600" data-prevent-swipe></div>
</section>
And model1.js file is the following:
document.addEventListener('DOMContentLoaded', function(event) {
window.requestAnimationFrame = (function() {
return window.requestAnimationFrame;
})();
function animateScene() {
requestAnimationFrame(animateScene);
renderScene();
}
function startScene() {
var canvas = document.getElementById('model1');
render = new THREE.WebGLRenderer({
antialias: true,
alpha: true});
render.setClearColor(0x191919, 0);
var canvasWidth = canvas.getAttribute('width');
var canvasHeight = canvas.getAttribute('height');
render.setSize(canvasWidth, canvasHeight);
canvas.appendChild(render.domElement);
scene = new THREE.Scene();
var aspect = canvasWidth / canvasHeight;
camera = new THREE.PerspectiveCamera(45, aspect);
camera.position.set(15, 10, 15);
camera.lookAt(scene.position);
scene.add(camera);
controls = new THREE.OrbitControls(camera);
controls.enableDamping = true;
controls.dampingFactor = 0.25;
controls.enableZoom = true;
// concrete columns
// define columns geometry
var columnGeometry = new THREE.BoxGeometry(0.25, 3, 0.4);
// define columns colors
function hsl(h, s, l) {
return (new THREE.Color()).setHSL(h, s, l);
}
// define columns position & pass data (geometry, color, position) to create foundation function
{
makeInstanceColumn(columnGeometry, hsl(0 / 8, 1, .5), 0, -0, 0);
}
// create columns
function makeInstanceColumn(columnGeometry, color, x, z, y) {
[THREE.BackSide, THREE.FrontSide].forEach((side) => {
var columnMaterial = new THREE.MeshPhongMaterial({
color,
opacity: 0.5,
transparent: true,
side,
});
var column = new THREE.Mesh(columnGeometry, columnMaterial);
scene.add(column);
column.position.set(x, z, y);
});
}
// light
function addLight(x, z, y) {
var color = 0xFFFFFF;
var intensity = 1;
var light = new THREE.DirectionalLight(color, intensity);
light.position.set(x, z, y);
scene.add(light);
}
addLight(-1, 2, 4);
addLight( 1, -1, -2);
}
function renderScene() {
controls.update();
render.render(scene, camera);
}
startScene();
animateScene();
renderScene();
});
It works, although I had to insert another slide between to consecutive 3D models. Otherwise they appear in the same slide (as an overlay) even though the "canvas"es are created in different "section"s. And also it does not work more than two times. However, the main problem is that I am unable to interact with the first model. The OrbitControl, seems to be linked with the second last 3D model, which slide I have not yet visualize but rotates, pans and zooms it.
I have read that there is a "slidechanged" event listener. I think that I should insert it somewhere in the code but I do not know how and where. I tried subtituting document.addEventListener('DOMContentLoaded', function(event) ... from the beginning of the script with "document.addEventListener('slidechanged', function(event) ... but it did not work. Silly me, I suppose I should use Reveal.on ... but I do not figure how.
BTW I am using a quite old version of three.js (r84). I do not need the full potential of three.js neither I can use it, because I need to show it in an old iPad (iOS 9.3.5) I know there is an example of combining three.js and reveal.js. It uses and even older version (r59) but I can not simply swap my scripts for its scripts, because it does not recognize some of the commands, e.g. BoxGeometry and other stuff.
I would like to do something similar, but I seek for guidance and advice. Does someone face something similar? Any ideas?
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.
Im making a threejs game.
Now, what should happen is, a ball that comes from z-position -5000 to 0.
This is how i create the mesh:
let geometry = new THREE.SphereGeometry(100, 32, 32);
let material = new THREE.MeshBasicMaterial({
map: loader.load("images/SoftballColor.jpg"), side: THREE.BackSide
});
mesh = new THREE.Mesh(geometry, material);
mesh.position.set(0,0, -5000);
In the render, I run this code (ball coming closer):
mesh.position.z += 50;
the ball comes to me, but in a straight line
What i would like to do, is do this in a bowl. Any ideas?
I'll add some images to make it more clear.
Thank you in advance.
Ball movement:
The Sinus function will help.
var how_high = 1000;
var start_position = new THREE.Vector3(0,0,-5000);
You know the z-value of ball's position, so, its y-value will be
ball.position.y = Math.sin(ball.position.z / start_position.z * Math.PI) * how_high;
jsfiddle example
I am trying to plot a graph in my application. I am trying to plot a graph having 4 points which needs to be joined by lines. I tried with so many libralies such as canvasXpress.. But could not able to find the solution.
I had tried this much.
http://jsfiddle.net/rohitghatol/tn9sm/
var render = function(){
renderer.render(scene,camera);
}
render();
$("#container").append(renderer.domElement);
Now I have to draw 4 points with joining lines.
I am not getting how to scale and plot the lines in the graph.
I have found the solution using Three.Js javascript library. Here's the snippet.
I had used the following code to create the grid helpers and it worked.
var gridXZ = new THREE.GridHelper(100, 10);
gridXZ.setColors(new THREE.Color(0x939393), new THREE.Color(0xbdbdbd));
gridXZ.position.set(100, -.1, 100);
scene.add(gridXZ);
var gridXY = new THREE.GridHelper(100, 10);
gridXY.position.set(100, 100, 0);
gridXY.rotation.x = Math.PI / 2;
gridXY.setColors(new THREE.Color(0x0066FF), new THREE.Color(0xCCE0F9));
scene.add(gridXY);
var gridYZ = new THREE.GridHelper(100, 10);
gridYZ.position.set(0, 100, 100);
gridYZ.rotation.z = Math.PI / 2;
gridYZ.setColors(new THREE.Color(0xCC9900), new THREE.Color(0xF5EBCC));
scene.add(gridYZ);
And for drawing the lines I had drawn the lines using three D points
// Line one
material = new THREE.LineBasicMaterial({
color: 0x000000
});
geometry = new THREE.Geometry();
geometry.vertices.push(
new THREE.Vector3(x1, y1, z1),
new THREE.Vector3(x2, y2, z2)
);
line = new THREE.Line(geometry, material);
scene.add(line);
Has anyone got experience on drawing a hollow circle with Pixi?, my current approach only works in the WebGL renderer which makes me think it's flawed, and i also need to fallback to the other rendered for other devices.
this.mathPiDub = 2 * Math.PI;
this.color = 0xFF00FF
var nuRad = 10;
this.pixiCircle.beginFill(this.color, 1);
this.pixiCircle.arc(0, 0, nuRad - 2, 0, this.mathPiDub, false);
this.pixiCircle.arc(0, 0, nuRad, 0, this.mathPiDub, false);
this.pixiCircle.endFill();
The 2D rendered gives me a full circle while the WebGL cuts the hole through.
What's the best approach?
The easiest way would be to use the built in drawCircle() with a lineStyle instead of a fill.
this.pixiCircle = new PIXI.Graphics();
this.pixiCircle.lineStyle(2, 0xFF00FF); //(thickness, color)
this.pixiCircle.drawCircle(0, 0, 10); //(x,y,radius)
this.pixiCircle.endFill();
stage.addChild(this.pixiCircle);
This will work for both WebGL and Canvas renderers
If you are using version 5, you can use the beginHole() and endHole() methods.
function createHollowCircle(radius, x_coordinate, y_coordinate) {
let circle = new Graphics();
circle.beginFill(color);
circle.drawCircle(0, 0, radius);
circle.beginHole();
circle.drawCircle(0, 0, radius - 0.1);
circle.endHole();
circle.endFill();
circle.x = x_coordinate;
circle.y = y_coordinate;
return circle;
}
Hollow Circles