QUESTION:
I load frontObject onto the scene. I get the centre of said object.
Then I move beyond the object by setting a new point that reuses all the object's centre coordinates except the Z axis (the z coordinate becomes 100), so that I can trace a raycaster from the outside of the character (frontObject) towards it and get the intersection point on the back of the character so that I may place a shield on that point.
Sadly, I get the following output:
CODE:
let box = new THREE.Box3().setFromObject(frontObject);
let sphere = box.getBoundingSphere();
let centerPoint = sphere.center;
backObject.position.set(0,132,-15);
console.log("CENTER POINT X: "+centerPoint.x);
console.log("CENTER POINT Y: "+centerPoint.y);
console.log("CENTER POINT Z: "+centerPoint.z);
centerPoint.z = 100;
var newCoordinate = shootRay(centerPoint, frontObject);
console.log("NEW POINT X: "+newCoordinate.x);
console.log("NEW POINT Y: "+newCoordinate.y);
console.log("NEW POINT Z: "+newCoordinate.z);
function shootRay(center, frontObject) {
var raycaster = new THREE.Raycaster();
var direction = new THREE.Vector3( 0, 0, 1 );
raycaster.ray.direction.copy( direction );
raycaster.ray.origin.copy( center);
var intersects = raycaster.intersectObject(frontObject);
console.log("GO");
if (intersects) {
var point = intersects.point;
console.log("POINT:"+point);
return point;
}
}
EDIT:
https://jsfiddle.net/Username100/y54kpe1h/66/
Your code is somewhat buggy. You'll want to read and address the warnings that you posted.
For var intersects = raycaster.intersectObjects(frontObject); frontObject needs to be an Array like [frontObject] or you need to use the singular intersectObject.
I also recommend using a debugger like the one built in to chrome to put a break point, instead of console.log('POINT , and then seeing what you're getting back from the raycast.
Let me know if this helps...
Related
I have an array of coordinates [x1,y1, x2, y2 ....] which represents a polygon on a html canvas. I am drawing the polygon using KonvaJS. I am trying to retrieve the scaled coordinates for the polygon where [x1, y1, x2, y2 ...] is the scaled coordinates.
I have tried the following:
Using JSTS https://github.com/bjornharrtell/jsts to add buffer to the coordinates.
Followed this snippet http://jsfiddle.net/qdv1n4yL/7/ and tried in integrate into my react/typescript app and created the snippet as shown below
function vectorCoordinates2JTS (polygon) {
var coordinates: any = [];
for (var i = 0; i < polygon.length; i++) {
coordinates.push(new Coordinate(polygon[i].x, polygon[i].y));
}
return coordinates;
}
function inflatePolygon(poly, spacing) {
var geoInput = vectorCoordinates2JTS(poly);
geoInput.push(geoInput[0]);
var geometryFactory = new GeometryFactory();
var shell = geometryFactory.createPolygon(geoInput);
var polygon = new BufferOp(poly);
console.log(polygon.getResultGeometry())
var inflatedCoordinates: any = [];
var oCoordinates;
oCoordinates = polygon.shell.points.coordinates;
console.log(oCoordinates.length)
for (let i = 0; i < oCoordinates.length; i++) {
var oItem;
oItem = oCoordinates[i];
inflatedCoordinates.push(Math.ceil(oItem.x), Math.ceil(oItem.y));
}
return inflatedCoordinates;
}
Here I am passing poly as [{x: 1, y:1}, {x:2, y:2}] and spacing as 1.5. It breaks on this line here polygon.getResultGeometry() with an error.
Also tried using this library to get the transformed coordinates http://turfjs.org/docs/#transformScale but it returns me a bunch of coordinates which i cant seem to use and get my end result as just a simple list of scaled coordinates.
My overall requirements for solving this problem is the polygon should have a cushioning like buffer around the original polygon which can be used to detect collision. I was able to get the collision detection between the polygon and a point working but been stuck on adding the scale/buffer for the past few days. Any help or follow up questions to this problem is highly appreciated. Thanks!
If you need mouse or touch events, for point hit detection, so to extend the edge of the polygon so that you get a larger area for collision detection, Konva has the hitStrokeWidth parameter. See point 2 on this docs page.
The gist is that Konva provides its own hit detection feature by drawing the shape in an off-screen canvas but inflating the stroke width. For example:
const line = new Konva.Line({
x: 50,
y: 100,
points: [0, 0, 50, 0, 50, 100, 0, 100],
tension: 1,
strokeWidth: 1,
hitStrokeWidth: 20,
stroke: 'black',
});
layer.add(line);
What this does is to draw the stroke with the given hitStrokeWidth size - in this case 20px. But only on the hit detection canvas offscreen. You can then avoid the need for the calculation you are attempting.
I'm trying to use part of a video as a texture in a Three.js mesh.
Video is here, http://video-processing.s3.amazonaws.com/example.MP4 it's a fisheye lens and I want to only use the part with actual content, i.e. the circle in the middle.
I want to somehow mask, crop or position and stretch the video on the mesh so that only this part shows and the black part is ignored.
Video code
var video = document.createElement( 'video' );
video.loop = true;
video.crossOrigin = 'anonymous';
video.preload = 'auto';
video.src = "http://video-processing.s3.amazonaws.com/example.MP4";
video.play();
var texture = new THREE.VideoTexture( video );
texture.minFilter = THREE.NearestFilter;
texture.magFilter = THREE.LinearFilter;
texture.format = THREE.RGBFormat;
var material = new THREE.MeshBasicMaterial( { map : texture } );
The video is then projected onto a 220 degree sphere, to give the VR impression.
var geometry = new THREE.SphereGeometry( 200,100,100, 0, 220 * Math.PI / 180, 0, Math.PI);
Here is a code pen
http://codepen.io/bknill/pen/vXBWGv
Can anyone let me know how I'm best to do this?
You can use texture.repeat to scale the texture
http://threejs.org/docs/#Reference/Textures/Texture
for example, to scale 2x on both axis
texture.repeat.set(0.5, 0.5);
In short, you need to update the UV-Map of the sphere so that the relevant area of your texture is assigned to the corresponding vertices of the sphere.
The UV-coordinates for each vertex define the coordinates within the texture that is assigned to that vertex (in a range [0..1], so coordinates (0, 0) are the top left corner and (1,1) the bottom right corner of your video). This example should give you an Idea what this is about.
Those UV-coordinates are stored in your geometry as geometry.faceVertexUvs[0] such that every vertex of every face has a THREE.Vector2 value for the UV-coordinate. This is a two-dimensional array, the first index is the face-index and the second one the vertex-index for the face (see example).
As for generating the UV-map there are at least two ways to do this. The probably easier way (ymmv, but I'd always go this route) would be to create the UV-map using 3D-editing software like blender and export the resulting object using the three.js exporter-plugin.
The other way is to compute the values by hand. I would suggest you first try to simply use an orthographic projection of the sphere. So basically, if you have a unit-sphere at the origin, simply drop the z-coordinate of the vertices and use u = x/2 + 0.5 and v = y/2 + 0.5 as UV-coordinates.
In JS that would be something like this:
// create the geometry (note that for simplicity, we're
// a) using a unit-sphere and
// b) use an exact half-sphere)
const geometry = new THREE.SphereGeometry(1, 18, 18, Math.PI, Math.PI)
const uvs = geometry.faceVertexUvs[0];
const vertices = geometry.vertices;
// compute the UV from the vertices of the sphere. You will probably need
// something a bit more elaborate than this for the 220degree FOV, also maybe
// some lens-distorion, but it will boild down to something like this:
for(let i = 0; i<geometry.faces.length; i++) {
const face = geometry.faces[i];
const faceVertices = [vertices[face.a], vertices[face.b], vertices[face.c]];
for(let j = 0; j<3; j++) {
const vertex = faceVertices[j];
uvs[i][j].set(vertex.x/2 + 0.5, vertex.y/2 + 0.5);
}
}
geometry.uvsNeedUpdate = true;
(if you need more information in either direction, drop a comment and i will elaborate)
I am trying to Extrude the Rectangle drawn in the canvas to Three.js canvas.
Here Blue one is 2d canvas drawing and Green is 3d
var Shape = new THREE.Shape();
Shape.moveTo(0,0,0);
for(var i=0;i<=point.length/2;i++)
{
Shape.lineTo(point[i],point[i+1]);
}
var ExtrusionSettings = {
curveSegments: 3,
bevelThickness:0, bevelSize: 0, bevelEnabled: false,
material: 0, extrudeMaterial: 1,amount: 10
};
var Geometry = new THREE.ExtrudeGeometry( Shape, ExtrusionSettings );
var Material = new THREE.MeshLambertMaterial({color: 0xff8800});
Material.side = THREE.DoubleSide;
Mesh = new THREE.Mesh(Geometry,Material);
Mesh.position.set(0,0,0);
Scene.add(Mesh);
The points is passed as an array which contain the x,y coordinates of the lines in the canvas
the points are passed by
function mouseDown(event)
{
Line[0] = event.pageX - this.offsetLeft;
Line[1] = event.pageY - this.offsetTop;
console.log("down");
}
function mouseUp(event)
{
Line[2] = event.pageX - this.offsetLeft;
Line[3] = event.pageY - this.offsetTop;
console.log("up");
var Width = Math.abs(Line[2] - Line[0]);
var Height = Math.abs(Line[3] - Line[1]);
Context.beginPath();
//Context.moveTo(Line[0], Line[1]);
//Context.lineTo(Line[2], Line[3]);
//Context.rect(Line[0],Line[1],Width,Height);
Context.lineWidth="5";
Context.strokeStyle="red";
var L1P1x = Line[0];
var L1P1y = Line[1];
var L1P2x = Line[0]+Width;
var L1p2Y = Line[1];
var L2P1x = Line[0]+Width;
var L2P1y = Line[1];
var L2P2x = Line[2];
var L2P2y = Line[3];
var L3P1x = Line[2];
var L3P1y = Line[3];
var L3P2x = Line[0];
var L3P2y = Line[1]+Height;
var L4P1x = Line[0];
var L4P1y = Line[1]+Height;
var L4P2x = Line[0];
var L4P2y = Line[1];
Context.moveTo(L1P1x,L1P1y);
Context.lineTo(L1P2x,L1p2Y);
Context.moveTo(L2P1x,L2P1y);
Context.lineTo(L2P2x,L2P2y);
Context.moveTo(L3P1x,L3P1y);
Context.lineTo(L3P2x,L3P2y);
Context.moveTo(L4P1x,L4P1y);
Context.lineTo(L4P2x,L4P2y);
Context.stroke();
Points.push(L1P1x,L1P1y,L1P2x,L1p2Y,L2P1x,L2P1y,L2P2x,L2P2y,L3P1x,L3P1y,L3P2x,L3P2y,L4P1x,L4P1y,L4P2x,L4P2y);
addMesh(Points);//points are passes to draw in 3d
//console.log(Points);
}
Not a fan of three.js but looking at your code you have some basic logic errors.
The short answer
Logic errors in the for loop! Change your first snippet to the following. You have Points in the second snippet and Point in the first. I have used point in the fix as I assume that is the correct name for the array of coordinates.
var i, len, mesh; // define all the vars you will use
len = point.length; // get the number of coordinates.
if(len > 1) { // make sure there are points.
shape.moveTo(point[0], point[1]); // move to the first point
for(i = 2; i < len; i += 2) { // iterate other points and lineTo them
shape.lineTo(point[i], point[i + 1]); // add the line.
}
mesh = new THREE.Mesh(
new THREE.ExtrudeGeometry( // Create geom
shape,
{ // extrusion settings.
curveSegments : 3,
bevelThickness : 0,
bevelSize : 0,
bevelEnabled : false,
material : 0,
extrudeMaterial : 1,
amount : 10
}
),
new THREE.MeshLambertMaterial({color: 0xff8800}) // material
);
mesh.position.set(0, 0 0); // position the mesh
scene.add(mesh); // add it to the scene
}
That will fix the bug. It was the for loop that was bad.
The long answer.
You seem to be a beginner so the long answer is to give you some longer term advice. (advice only as there are no rules).
Creating the shape. You had...
var Shape = new THREE.Shape();
Shape.moveTo(0,0,0);
for(var i=0;i<=point.length/2;i++)
{
Shape.lineTo(point[i],point[i+1]);
}
Now with my pedantic eye
Never name vars with a capital. Capitaitals are reserved for named objects. Though in this case you are safe, using the name Shape in a differing scopy could well overwrite the object constructor.
var Shape = new THREE.Shape(); // you had
should be
var shape = new THREE.shape();
Capitals are only for objects that you can use the new token with, Acronyms, or constants. This is not a trivial convention as all of Javascript's inbuilt naming uses it and I have yet to find a popular framework that does not use it. DON'T CAPITALIZE, it is a bad habit in javascript and will cause endless hours looking for simple syntax bugs.
The THREE.Shape object only deals with 2D paths. You had
// 3 coordinates for for a 2D path???
Shape.moveTo(0,0,0); // remove this line it is not needed
The last 0 is ignored, ( I checked the THREE.js source code) and that is not the error.
You error is here in the following.
for(var i=0;i<=point.length/2;i++) // Only half the points ??
{ // you then line to x,y
Shape.lineTo(point[i],point[i+1]);
// Next lineTo will be y,x then x,y messing everything up.
}
point refers the the array of numbers representing the x and y coordinates of the path. It is organised with the x coord, then y then x then y.
The number of items in the array is the number of 2D points * 2. The * 2 is because there is a X and a ``Y for each point.
So need to iterate the point array correctly.
Step by step.
var i, len; // always put your vars declarations at the top
len = point.length; // I like to get the length before the loop.
The for loop needs to step by 2 as the are two entries for each point.
for(i = 0; i < len; i += 2) { //Put the { at the end. Saves space and is easier to read.
Check for the first point as you need to moveTo (note answer is slightly different)
if(i === 0) {
Add the point to the path (shape).
shape.moveTo(point[i], point[i + 1]);
Then the other points
} else {
shape.lineTo(point[i], point[i + 1]);
}
This will create the correct shape that you can then use to extrude.
Put all together as I would do it.
// where do you define scene. It should be lowercase
var i, len, mesh;
len = point.length;
if(len > 1) { // check if there are points (2 or more.
shape.moveTo(point[0], point[1]); // do the first point outside the for loop
// this saves having to do the if statement
// for each point
for(i = 2; i < len; i += 2) { // iterate points starting at the second
shape.moveTo(point[i], point[i + 1]); // add the line.
}
You had
// Such a long name for a one of abd just a mess
// Your code
//var ExtrusionSettings = {
// curveSegments: 3,
// bevelThickness:0, bevelSize: 0, bevelEnabled: false,
// material: 0, extrudeMaterial: 1,amount: 10
//};
// unless it will be used again put it inline
// Bad naming for the rest
// Your code
//var Geometry = new THREE.ExtrudeGeometry( Shape, ExtrusionSettings );
//var Material = new THREE.MeshLambertMaterial({color: 0xff8800});
//Material.side = THREE.DoubleSide;
//Mesh = new THREE.Mesh(Geometry,Material);
//Mesh.position.set(0,0,0);
//Scene.add(Mesh);
Replace it all with
// Material is used once so no need to create var for it
// Removed Material.side = THREE.DoubleSide; // assuming this is debug code only
// you had Mesh without var. That made it global scope. Never use a var without defining it first with the var token
var mesh = new THREE.Mesh( // define and assign mesh
new THREE.ExtrudeGeometry( // Create geom// indent arguments for readability
shape,
{ // extrusion settings.
curveSegments : 3, // line it all up so you can read it quickly
bevelThickness : 0,
bevelSize : 0,
bevelEnabled : false,
material : 0,
extrudeMaterial : 1,
amount : 10
}
),
new THREE.MeshLambertMaterial({color: 0xff8800})
);
mesh.position.set(0,0,0); // position the mesh
scene.add(mesh); // add it to the scene
// note the lowercase scene for the object instance scene. Need to chage that where you create it.
} // end of if(len > 1){
There is a lot of pedantic stuff there but you are clearly new to programming and bad habits are hard to break so start with good ones. Bugs and debugging are the worst part of programming. Debugging is the single most time consuming part of all programming (even for experienced professionals). The single biggest cause of bugs in all programing languages is bad style and or messy code. Writing clean consistent code makes your code easier to read and hence easier to debug. Try finding a missing { in 5000 lines of `{''s or a capital where a lowercase character should be (Hours debugging code while the simple error is right in front of you hidden in a mess can make or break being a coder)
Hope this helped..
I have written a small 2D game in javascript that uses a grid where the player starts at position [0,0] and can move an almost infinite distance in either direction.
Now I want to implement A* pathfinding, but I'm having some problems finding the best way to store the world with all it's different obstacles, enemies and terrain. This is what I have tried or thought about so far.
Array of arrays
Here I store the world in an array of arrays [x][y].
var world = [[]];
world[312][11] = 0;
world[312][12] = 0;
world[312][13] = 1;
world[312][14] = 1;
...
This works great with A* pathfinding! It's easy and very fast to access a specific coordinate and populate the world. In the example above I just store passable (0) or impassable (1) terrain, but I can store pretty much whatever I want there. However, this doesn't work very well with negative coordinates like if my players is at [-12][-230]. Negative keys in a javascript array isn't actually part of the array, they won't be included in world.length or world[3].length and from what I understand, it's overall bad practice and might have some impact on the performance as well. I read somewhere that if you are using negative keys in your array, you are doing it wrong.
I would still not pass the entire world into the A* function for obvious reasons. Just a small part close to my player, but the coordinates would correspond to the positions in the array which is easy to work with.
A separate array of arrays just for A* pathfinding
This is where I'm at right now. I have a separate 50x50 grid called pathMap = [[]], that is only used for pathfinding.
var pathMap = [[]];
pathMap[0][0] = 0;
pathMap[0][1] = 0;
pathMap[0][2] = 1;
pathMap[0][3] = 1;
...
It starts at pathMap[0][0] and goes to pathMap[50][50] and is working as an overlay on my current position where I (as the player) will always be in the center position. My real coordinates may be something like [-5195,323], but it translates to pathMap[25][25] and everything close to me is put on the pathMap in relation to my position.
Now this works, but it's a huge mess. All the translations from one coordinate to another back and forth makes my brain hurt. Also, when I get the path back from A*, I have to translate each step of it back to the actual position my element should move to in the real world. I also have to populate the same object into 2 different grids every update which hurts performance a bit as well.
Array of objects
I think this is where I want to be, but I have some issues with this as well.
var world = [];
world[0] = { x: -10, y: 3, impassable: 0 };
world[1] = { x: -10, y: 4, impassable: 0 };
world[2] = { x: -10, y: 5, impassable: 1 };
world[3] = { x: -10, y: 6, impassable: 1 };
...
Works great with negative x or y values! However, it's not as easy to find for instance [10,3] in this array. I have to loop through the entire array to look for an object where x == 10 and y == 3 instead of the very easy and fast approach world[10][3] in the first example. Also, I can't really rely on the coordinates being in the right order using this version, sorting becomes harder, as does other things that was a lot easier with the array of arrays.
Rebuild the game to always be on the positive side
I would prefer not to do this, but I have considered placing the players starting position at something like [1000000,1000000] instead, and making negative coordinates off limits. It seems like a failure if I have to remove the vision I have of endlessness just to make the pathfinding work with less code. I know there will always be some upper or lower limits anyways, but I just want to start at [0,0] and not some arbitrary coordinate for array related reasons.
Other?
In javascript, is there another option that works better and is not described above? I'm very open to suggestions!
Is there a best practice for similar cases?
You have three coordinates system you must distinguish :
the world coordinates.
the world model / path-finding (array) coordinates.
the screen coordinates.
The screen coordinates system depends upon :
the viewport = the canvas. (width, height in pixels).
a camera = (x,y) center in world coordinates + a viewWidth (in world coordinates).
To avoid headaches, build a small abstraction layer that will do the math for you.
You might want to use Object.defineProperty to define properties, that will provide a fluent interface.
var canvas = ... ;
var canvasWidth = canvas.width;
var canvasHeigth = canvas.heigth;
var world = {
width : 1000, // meters
height : 1000, // meters
tileSize : 0.5, // height and width of a tile, in meter
model : null, // 2D array sized ( width/tileSize, XtileSize )
};
// possibles world coordinates range from -width/2 to width/2 ; - height/2 height/2.
var camera = {
x : -1,
y : -1,
viewWidth : 10, // we see 10 meters wide scene
viewHeight : -1 // height is deduced from canvas aspect ratio
};
camera.viewHeight = camera.viewWidth * canvasWidth / canvasHeight ;
Then your character looks like :
// (x,y) is the center of the character in centered world coordinates
// here (0,0) means (500,500) world coords
// (1000,1000) array coords
// (320, 240) screen coords in 640X480
function /*Class*/ Character(x, y) {
var _x=x;
var _y=y;
var _col=0;
var _row=0;
var _sx=0.0;
var _sy=0.0;
var dirty = true;
Object.defineProperty(this,'x',
{ get : function() {return _x; }
set : function(v) { _x=v;
dirty=true; } });
Object.defineProperty(this,'x',
{ get : function() {return _y; }
set : function(v) { _y=v;
dirty=true; } });
Object.defineProperty(this,'col',
{ get : function() {
if (dirty) updateCoords();
return _col; } });
Object.defineProperty(this,'row',
{ get : function() {
if (dirty) updateCoords();
return _row; } });
Object.defineProperty(this,'sx',
{ get : function() {
if (dirty) updateCoords();
return _sx; } });
Object.defineProperty(this,'sy',
{ get : function() {
if (dirty) updateCoords();
return _sy; } });
function updateCoords() {
_row = ( ( _x + 0.5 * world.width )/ world.tileSize ) | 0 ;
_col = ( ( _x + 0.5 * world.height )/ world.tileSize ) | 0 ;
_sx = canvasWidth * ( 0.5 + ( _x - camera.x ) / camera.viewWidth ) ;
_sy = canvasHeight * ( 0.5 + ( _y - camera.y ) / camera.viewHeight ) ;
dirty = false;
}
}
I want to animate a path (actually a set of paths, but I'll get to that) along a curved path.
RaphaelJS 2 removed the animateAlong method, for reasons I haven't been able to discern. Digging into the Raphael documentation's gears demo as abstracted by Zevan, I have got this far:
//adding a custom attribute to Raphael
(function() {
Raphael.fn.addGuides = function() {
this.ca.guide = function(g) {
return {
guide: g
};
};
this.ca.along = function(percent) {
var g = this.attr("guide");
var len = g.getTotalLength();
var point = g.getPointAtLength(percent * len);
var t = {
transform: "t" + [point.x, point.y]
};
return t;
};
};
})();
var paper = Raphael("container", 600, 600);
paper.addGuides();
// the paths
var circ1 = paper.circle(50, 150, 40);
var circ2 = paper.circle(150, 150, 40);
var circ3 = paper.circle(250, 150, 40);
var circ4 = paper.circle(350, 150, 40);
var arc1 = paper.path("M179,204c22.667-7,37,5,38,9").attr({'stroke-width': '2', 'stroke': 'red'});
// the animation
// works but not at the right place
circ3.attr({guide : arc1, along : 1})
.animate({along : 0}, 2000, "linear");
http://jsfiddle.net/hKGLG/4/
I want the third circle to animate along the red path. It is animating now, but at a distance from the red path equal to the third circle's original coordinates. The weird thing is that this happens whether the transform translate in the along object is relative (lowercase "t") or absolute (uppercase "T"). It also always animates in the same spot, even if I nudge it with a transform translation just before the animate call.
Any help very appreciated. I just got off the boat here in vector-land. Pointers are helpful--a working fiddle is even better.
You're just a hop, skip, and jump away from the functionality that you want. The confusion here concerns the interaction between transformations and object properties -- specifically, that transformations do not modify the original object properties. Translating simply adds to, rather than replaces, the original coordinates of your circles.
The solution is extremely straightforward. In your along method:
this.ca.along = function(percent) {
var box = this.getBBox( false ); // determine the fundamental location of the object before transformation occurs
var g = this.attr("guide");
var len = g.getTotalLength();
var point = g.getPointAtLength(percent * len);
var t = {
transform: "...T" + [point.x - ( box.x + ( box.width / 2 ) ), point.y - ( box.y + ( box.height / 2 ) )] // subtract the center coordinates of the object from the translation offset at this point in the guide.
};
return t;
Obviously, there's some room for optimization here (i.e., it might make sense to create all your circles at 0,0 and then translate them to the display coordinates you want, avoiding a lot of iterative math). But it's functional... see here.
One other caveat: the ...T translation won't effect any other transforms that have already been applied to a given circle. This implementation is not guaranteed to play nicely with other transforms.