I have an object(LineA) that is composed of 4 children and sub-children. They are Meshes(MeshLineMaterial) and ArrowHelpers. Then I have another set of objects(Other Lines) of the same kind.
Sample object:
castShadow: false
children: Array(2)
0: ja {uuid: "B883597B-392D-41EB-A306-33A2F065A5B1", name: "PathA", type: "Mesh", parent: Hc, children: Array(0), …}
1: tb {uuid: "8B507A56-437C-47C5-9A0A-43DE9D8DE0D4", name: "", type: "Object3D", parent: Hc, children: Array(2), …}
length: 2
__proto__: Array(0)
frustumCulled: true
layers: Pf {mask: 1}
matrix: U {elements: Array(16)}
matrixAutoUpdate: true
matrixWorld: U {elements: Array(16)}
matrixWorldNeedsUpdate: false
nObjType: "Path"
name: "Path"
parent: wd {uuid: "20293B1F-868F-4A06-9A24-AF14738955A8", name: "", type: "Scene", parent: null, children: Array(121), …}
position: n {x: 0, y: 0, z: 0}
quaternion: ua {_x: 0, _y: 0, _z: 0, _w: 1,
_onChangeCallback: ƒ}
receiveShadow: false
renderOrder: 0
rotation: Qb {_x: 0, _y: 0, _z: 0,
_order: "XYZ",
_onChangeCallback: ƒ}
scale: n {x: 1, y: 1, z: 1}
selectable: true
type: "Group"
up: n {x: 0, y: 1, z: 0}
userData: {}
uuid: "CC27818D-5EEE-4A89-A39C-7F56C8C8F9BB"
visible: true
eulerOrder: (...)
id: 561
modelViewMatrix: U {elements: Array(16)}
normalMatrix: Y {elements: Array(9)}
useQuaternion: (...)
__proto__: B
I am trying to create a system where when I create LineA, it shouldn't collide/cross with Other Lines.
I have tried the bounding box and raycaster:
function collision()
{
var originPoint = LineA.position.clone();
var bb=new THREE.BoxHelper(LineA, 0x00ff00);
bb.geometry.computeBoundingBox();
var collidableMeshList = OtherLines;
for (var vertexIndex = 0; vertexIndex < bb.geometry.attributes.position.array.length; vertexIndex++)
{
var ray = new THREE.Raycaster( bb.position, bb.geometry.attributes.position.array[vertexIndex] );
var collisionResults = ray.intersectObjects( collidableMeshList );
if ( collisionResults.length > 0)
{
console.log("true");
hit = true;
}
}
}
I tried referring to this example
function detectCollisionCubes(bb, Line2bb){
bb.geometry.computeBoundingBox(); //not needed if its already calculated
Line2bb.geometry.computeBoundingBox();
bb.updateMatrixWorld();
Line2bb.updateMatrixWorld();
var box1 = bb.geometry.boundingBox.clone();
box1.applyMatrix4(bb.matrixWorld);
var box2 = Line2bb.geometry.boundingBox.clone();
box2.applyMatrix4(Line2bb.matrixWorld);
return box1.intersectsBox(box2);
}
None of them seems to be working in my case.
They show no error.
Any help would be greatly appreciated.
Related
I'm trying to get the (load in order) example to work. Here is my code.
I load snap.js and then my code in loadsvg.js it has my plugin. Then main.js with my code calling the function. I get a error in console that says "base.loadFilesDisplayOrdered is not a function". So it can't find it any help will be appreciated. Thanks
Snap.plugin( function( Snap, Element, Paper, global ) {
function addLoadedFrags( whichSVG, fragList, runWhenFinishedFunc ) { // This is called once all the loaded frags are complete
for( var count = 0; count < fragList.length; count++ ) {
myEl = whichSVG.append( fragList[ count ] );
}
runWhenFinishedFunc();
}
Paper.prototype.loadFilesDisplayOrdered = function( list, afterAllLoadedFunc, onEachElementLoadFunc ) {
var image, fragLoadedCount = 0, listLength = list.length, fragList = new Array(), whichSVG = this;
for( var count = 0; count < listLength; count++ ) {
(function() {
var whichEl = count,
fileName = list[ whichEl ],
image = Snap.load( fileName, function ( loadedFragment ) {
fragLoadedCount++;
onEachElementLoadFunc( loadedFragment, fileName );
fragList[ whichEl ] = loadedFragment;
if( fragLoadedCount >= listLength ) {
addLoadedFrags( whichSVG, fragList, afterAllLoadedFunc );
}
} );
})();
}
};
});
Then i call it in main.js
var base = Snap("#svgout");
console.log(base);
var myLoadList = [ "svgs/layer.svg", "svgs/cutout.svg" ];
var myDisplayList = { "Bird.svg": "zoom", "Dreaming_tux.svg": "fade" };
base.loadFilesDisplayOrdered( myLoadList, onAllLoaded, onEachLoaded );
The problem is, you are trying to create Snap into a div element. Snap only works on SVG elements.
Change,
<div id="svgout"></div>
to
<svg id="svgout"></svg>
And the error should go away.
jsfiddle
Firstly a little elaboration of the project I'm working on. I have started building a 'map maker' for a 2d game I am working on. The project is just for fun and has proven so far to be a great way to learn new things.
I recently showed my working map maker code to a friend who suggested it would be much more re-usable if I restructured the project to be more OOR, which I am now attempting.
The problem I have is when I add a 'Guild' instance to my map, the first one works fine, but the second causes a type error that is giving me a headache!
I will post all of the relevant code from the different files below, but the overall structure is as follows:
Map.js = Map class file, container for setting the map overall size and iterating over (and placing) map objects.
MapObject.js = Class file for simple map objects such as walls, contains the position and icon properties.
Guild.js = Class file, extends MapObject.js, this is where my problem seems to be, adds an additional 'MapIcon' and will have other features such as levels and names etc.
map-maker.js = Main file for generating the map-maker page, utilises the above class files to create the map.
Below is the code used to create an instance of 'Guild' on my map:
map-maker.js (creating the map / map object / guild instances)
// Initialise a new instance of map class from Map.js using the user
provided values for #mapX and #mapY.
var currentMap = new Map(XY,40);
// Get the user box volume * map boxsize from Map.js
currentMap.calcDimensions();
// Create a Map Object and push it to the currentMap with its position.
function createMapObject(x,y,floor){
currentMap.objects.push(new MapObject(x,y,floor));
}
// Create a Guild Object (extension of Map Object) and push it to the currentMap with its position.
function createGuildObject(x,y,floor){
currentMap.objects.push(new Guild(x,y,floor));
}
....
case 13: // Enter Key (Submit)
unhighlightTools();
currentMap.drawMap();
if(currentFloor != null){
currentFloor.hasFloor = true;
if(currentFloor.tileName == "Guild"){
createGuildObject(currentFloor.position.x,currentFloor.position.y,currentFloor);
}else {
createMapObject(currentFloor.position.x,currentFloor.position.y,currentFloor);
}
console.log("Map object created at - X:"+currentFloor.position.x+" Y:"+currentFloor.position.y);
}
currentFloor = [];
highlightTools();
break;
}
Guild.js (constructor and assigning map icon)
class Guild extends MapObject {
constructor(x,y,floor) {
super(x,y,floor);
this.levels = [];
}
mapIcon() {
this.mapIcon = new Image();
this.mapIcon.src = "../images/mapSprites/obj-1.png"
return this.mapIcon;
}
}
MapObject.js (position setup and constructor)
class MapObject {
constructor(x,y,floor) {
this.position = {x, y};
this.icon = this.wallFloorIcons(floor);
}
wallFloorIcons(floor) {
this.img = new Image();
this.name = "";
this.name += (floor.wallNorth) ? 'n' : '';
this.name += (floor.wallEast) ? 'e' : '';
this.name += (floor.wallSouth) ? 's' : '';
this.name += (floor.wallWest) ? 'w' : '';
this.name = 'wall-'+this.name+'.png';
if(this.name == 'wall-.png'){
this.img.src = "../images/mapSprites/floor.png";
}else {
this.img.src = "../images/mapSprites/"+this.name;
}
return this.img;
}
getIcon() {
return this.img;
}
}
Map.js (processing the objects at a given location and drawing the canvas)
class Map {
// Map Width / Height and number of boxes. Used to generate map and zoom level.
constructor(wh, boxSize) {
this.size = wh;
this.width = wh[0];
this.height = wh[1];
this.boxSize = boxSize;
this.objects = [];
this.boxes = wh[0];
}
// Calculates the width and height * boxSize for rendering the canvas.
calcDimensions(){
this.realX = Math.floor(this.width * this.boxSize);
this.realY = Math.floor(this.height * this.boxSize);
this.realX = parseInt(this.realX,10);
this.realY = parseInt(this.realY,10);
this.realXY = [
this.realX,
this.realY
];
return this.realXY;
}
// Draws the canvas, grid and adds the objects that belong to the map.
drawMap(){
var self = this;
self.canvas = document.getElementById("canvas");
self.c = self.canvas.getContext("2d");
self.background = new Image();
self.background.src = "../images/mapSprites/oldPaperTexture.jpg";
// Make sure the image is loaded first otherwise nothing will draw.
self.background.onload = function(){
self.c.drawImage(self.background,0,0);
self.fillMap();
}
}
fillMap(){
var self = this;
self.c.lineWidth = 1;
self.c.strokeStyle = 'black';
self.c.fillStyle = "rgba(255, 255, 255, 0.2)";
for (var row = 0; row < self.boxes; row++) {
for (var column = 0; column < self.boxes; column++) {
var x = column * self.boxSize;
var y = row * self.boxSize;
self.c.beginPath();
self.c.rect(x, y, self.boxSize, self.boxSize);
self.c.stroke();
self.c.closePath();
for (var i=0; i<self.objects.length; i++) {
var floor = self.objects[i];
if (floor.position.x == column && floor.position.y == row) {
if (self.objectsAtPosition({x:floor.position.x, y:floor.position.y}) != null) {
var mapObjects = self.objectsAtPosition({x:floor.position.x, y:floor.position.y})
for (var mapObject of mapObjects) {
this.c.drawImage(mapObject.getIcon(), x, y, self.boxSize, self.boxSize);
console.log(mapObject);
if(mapObject instanceof Guild){
console.log(mapObject);
this.c.drawImage(mapObject.mapIcon(), x, y, self.boxSize, self.boxSize);
}
}
}
}
}
}
}
}
deleteObject(pos){
this.objectsAtPosition(pos);
for( var i = 0; i < this.objects.length; i++){
if(this.objects[i] == this.objs){
delete this.objects[i];
this.objects.splice(i,1);
}
}
}
objectsAtPosition(position) {
var objs = [];
for (var o of this.objects) {
if (o.position.x == position.x && o.position.y == position.y) {
objs.push(o);
}
}
return objs;
}
}
When I run the code, this is my error:
Uncaught TypeError: mapObject.mapIcon is not a function
at Map.fillMap (Map.js:70)
at Image.self.background.onload (Map.js:39)
The error comes after I add 1 guild then try to add any other map object. Guild or otherwise.
Sorry if this question is a little vague, I'm still learning (as you can see :p).
Thanks for your time!
Earl Lemongrab
Got a solution from a friend in the end.
The issue was that I was reassigning this.mapIcon = new Image() so it exploded when it was called a second time.
Feel pretty silly for not spotting it.
Thanks for the help everyone.
i am trying to change textures / colors of an object onclick (by clicking on separate cubes )
I'm able to change the color of an object only once (even though i use a for-loop ) and provided that no previous textures or colors on the object.
However, I'm not able to change the color if the object already has an existing color.
Do i need to add some needsUpdate ? I did but no luck.. please have a look on my onclick function.
EventsControls.attachEvent('onclick', function() {
var colors = ['White', 'blue', 'gold'];
for (var i = 0; i < colors.length; i++) {
object.traverse(function(child) {
if (child instanceof THREE.Mesh) {
if (child.material.name == "Sofa_Leather") {
child.material = colors[i]; // array elements are already defined
child.material.needsUpdate = true;
child.material.buffersNeedUpdate = true;
child.material.uvsNeedUpdate = true;
child.receiveShadow = true;
}
}
})
}
});
Kindly let me know where 'im going wrong. Thank you.
//retried but could manage to change only one color with the below code, i think im using the needsUpdate in a wrong way.
var index=0;
var colors=[0xfffeef,0xffff00,0x000fff];
object.traverse( function( child ) { if ( child instanceof THREE.Mesh ) {
if (child.material.name == "Sofa_Leather") {
if(index == colors.length) index = 0;
child.material.color.setHex(colors[index++]);
child.material.needsUpdate = true;
child.receiveShadow = true;
} }
})
I believe you can do this by using:
material.color.setHex(0xffffff * Math.random()); // set to random color
EDIT:
Well yeah sure you can do this also:
var colors = [0xff0000, 0x00ff00, 0x0000ff]; // red, green and blue
if(index == colors.length) index = 0;
line.material.color.setHex(colors[index++]);
See this updated jsfiddle
I'm using Three.js on an app, currently I'm having issues with the object picking. Some objects are not getting intersected by the rays, but only on certain camera rotations. I'm trying to debug the code and to draw the ray.
I'm using this methods in my code(canvas is a namespace for the Three objects):
C.getXY = function(e) {
var click = {};
click.x = ( e.clientX / window.innerWidth ) * 2 - 1;
click.y = - ( e.clientY / window.innerHeight ) * 2 + 1;
return click;
};
C.doPicking = function(e) {
var picked = false;
if (canvas.boundingBox !== null) {
canvas.scene.remove(canvas.boundingBox);
}
var projector = new THREE.Projector(), click = C.getXY(e);
var ray = projector.pickingRay(new THREE.Vector3(click.x, click.y, 0), canvas.camera);
ray.linePrecision = 0.00000000000000001;
ray.precision = 0.00000000000000001;
var intersects = ray.intersectObjects(canvas.scene.children);
if (intersects.length > 0) {
var i = 0;
var ids = [];
while (i < intersects.length) {
if (intersects[i].object.visible) {
//Object is picked
}
++i;
}
}
};
My question is...are other points to consider in the debuging process?
Ooops...forgot to add mesh.geometry.computeFaceNormals(); before adding the meshes to the scene. Now the picking is working normally.
I have a multiple models in Collada format (knight.dae & archer.dae).
My problem is that i can't get them all to animate(lets say Idle with is 2-3frames).
When i load the scene i either get only one animated model and one stil model(no animation,no nothing,it's like he's being modeled in 3ds max).
I know my problem is with the skin and morphs but i searched alot and didnt find an answer and due to my lack of experience my attempts have failed so far.
Help pls!
//animation length of the model is 150(and it hosts 4 different animations)
var startFrame = 0, endFrame = 150, totalFrames = endFrame - startFrame, lastFrame;
var urls = [];
var characters = [];
urls.push('3D/archer/archer.dae');
urls.push('3D/archer/archer.dae');
//here's the loader
loader = new THREE.ColladaLoader();
loader.options.convertUpAxis = true;
for (var i=0;i<urls.length;i++) {
loader.load(urls[i],function colladaReady( collada ){
player = collada.scene;
player.scale.x = player.scale.y = player.scale.z =10;
player.position.y=115;
player.position.z=i*200;
player.updateMatrix()
skin = collada.skins [ 0 ];
//skinArray.push(skin);;
var mesh=new THREE.Mesh(new THREE.CubeGeometry(10,20,10,1,1,1));
player.add(mesh);
characters.push(mesh);
scene.add( player );
});
}
//i added the cube because i use raycaster and it doesnt detect collada obj
// Here is where i try my animation.
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
update();
renderer.render(scene,camera);
}
function update() {
var delta = clock.getDelta();
delta = delta / 2;
if ( t > 1 ) t = 0;
if ( skin )
{
skin.morphTargetInfluences[lastFrame] = 0;
var currentFrame = startFrame + Math.floor(t*totalFrames);
skin.morphTargetInfluences[currentFrame] = 1;
t += delta;
lastFrame = currentFrame;
}
}
Try something like this.... at the beginning:
var skins = [];
At your collada callback, something you already seem to have thought about:
skins.push(collada.skins[0]);
At your renderer, instead of the current if (skin) clause:
t += delta;
lastFrame = currentFrame;
var currentFrame = startFrame + Math.floor(t*totalFrames);
for (var i = 0; i < skins.length; i++) {
var skin = skins[i];
if (skin) {
skin.morphTargetInfluences[lastFrame] = 0;
skin.morphTargetInfluences[currentFrame] = 1;
}
}
Point being, you need to loop all the skins in the update() function. I didn't check the frame handling code very carefully as that was not the question.. If your skins have different amount of frames you need to take those into account in your code (maybe make the lastFrame, currentFrame etc variables to arrays matching the skins array).
if ( skinArray[0] && skinArray[1] )
{
skinArray[0].morphTargetInfluences[lastFrame] = 0;
skinArray[1].morphTargetInfluences[lastFrame] = 0;
var currentFrame = startFrame + Math.floor(t*totalFrames);
skinArray[0].morphTargetInfluences[currentFrame] = 1;
skinArray[1].morphTargetInfluences[currentFrame] = 1;
t += delta;
lastFrame = currentFrame;
}
I came up with this code,it does the job but i just don't like it,mainly because it feels it's hardcoded.So if any of you guys can come up with a more elegant solution i'd be more then happy.