Given 200+ vertices generated by the AutoCad API in the form of an array of vectors {X:,Y:,Z:}, I am trying to render them in THREE.js with no luck.
For now I am making all the possible permutations for the number 200 and connecting all the vertices together - which I don't think is the way this is done as it gives over 200k faces.
EDIT: My AutoCAD code is getting all the vertices and then it tries to get it's connected vertices' ids (vertex1 and vertex2). GetHashCode() doesn't work though. The problem isn't that it returns huge id numbers like 148335760 and 682610240. The problem is that these ids are not unique and they happen to be defined and they are not connected to any other vertices.
AutoCAD code:
//data structures for serialisation
public class EdgeMe
{
public int vertex1;
public int vertex2;
}
public class VertexMe
{
public int id;
public Point3d Point;
public List<EdgeMe> Edges = new List<EdgeMe>();
}
public class DataMe{
public Extents3d extents;
public string layer;
public List<VertexMe> points = new List<VertexMe>();
}
//...
// Get each Solid3d in modelspace and add its extents
// to the list
foreach (var id in ms)
{
var obj = tr.GetObject(id, OpenMode.ForRead);
var sol = obj as Solid3d;
DataMe dataMe = new DataMe();
if (sol != null)
{
dataMe.extents = sol.GeometricExtents;
dataMe.layer = sol.Layer;
using (var brep = new Autodesk.AutoCAD.BoundaryRepresentation.Brep(sol))
{
foreach (var vertex in brep.Vertices)
{
VertexMe vertexMe = new VertexMe();
vertexMe.Point = vertex.Point;
vertexMe.id = vertex.Brep.GetHashCode();
foreach(var edge in vertex.Edges)
{
EdgeMe edgeMe = new EdgeMe();
edgeMe.vertex1 = edge.Vertex1.Brep.GetHashCode();
edgeMe.vertex2 = edge.Vertex2.Brep.GetHashCode();
vertexMe.Edges.Add(edgeMe);
}
dataMe.points.Add(vertexMe);
}
}
}
sols.Add(dataMe);
}
Javascript code:
var faces = function(vertices) {
var results = [];
var vertex = [0, 1, 2];
results.push(vertex.slice());
while(true) {
vertex[2]++;
if(vertex[2] > vertices) {
vertex[1]++;
if(vertex[1] >= vertices) {
vertex[0]++;
vertex[1] = vertex[0] + 1;
if(vertex[0] > vertices - 2)
return results;
}
vertex[2] = vertex[1] + 1;
}
results.push(vertex.slice());
}
};
var generate = function( ... ) {
// Process each box, adding it to the scene
for (var sol in sols) {
var s = sols[sol];
var vertices = [];
for(var vertix in s.points) {// deserialize
vertix = s.points[vertix];
vertices.push(new THREE.Vector3(vertix.X, vertix.Y, vertix.Z));
}
var holes = [];
var triangles, mesh;
var geometry = new THREE.Geometry();
geometry.vertices = vertices;
var xfaces = faces(vertices.length);
for(var i = 0; i < xfaces.length; i++) {
geometry.faces.push( new THREE.Face3( xfaces[i][0], xfaces[i][1], xfaces[i][2] ));
}
geometry.computeFaceNormals();
geometry.computeVertexNormals();
mesh = new THREE.Mesh( geometry, customimg );
mesh.rotation.set( Math.PI/2, 0, 0);
root.add(mesh);
}
}
Regards,
Ioan
You cannot do what you are doing. You are trying to guess the connectivity info. That should not be guess work. The application needs to return the connectivity info to you.
Instead I would suggest you use Forge Model Derivative API to extract the viewable from your file. Once you have the file translated, you can view it using the Viewer (which is based on Three.js, but optimized for engineering files). You'll find several examples at this Github.
Related
Having problems emmiting 2 arrays to the client.
I have 2 objects and all instances are created on Server side . The socket.emit is from what I understood done on the Server 25s/s and per socket that exist.
Now i created bullets in each ship and want to get these to the client.
The thing that makes me headache is I am waiting on the Client side with socket.on per ship which is a 1:1 per each emitted socket from the server.
And now come a 1:n per each socket on the Server that is shooting the bullets. Can I actually emit 2 array with 2 emit execution or does it Need to be ohne emit from each socket with all data in one array?
My Problem is that the array bullet doesnt exist on the Client side !
So my ship values are at the client side anyhow I dont have a bullet array on the Client side
I tested it with a draw in a fixed x and y
for (var i in bullet){
ctx.fillText("X",100,100);
}
but the client draws nothing means I have no array at the client side. Also did a alert if I would have more then 10 bullets but even that one doesn’t pop up
Appreciate any help
App.js
for(var q in SOCKET_LIST){
var socket = SOCKET_LIST[q];
var f =0;
for(var k = 0;k<allbullets.length;k++)
{
if (allbullets[k].user_id === q)
{
packbul= {
x:allbullets[k].xtype.x,
y:allbullets[k].xtype.y,
userid:allbullets[k].user_id,
}// array end
}
f++;
} //end for
socket.broadcast.emit('newBullet',packbul);
if (allobjects[q] === undefined)
{
}
else{
console.log("q:"+q);
pack[q] = {
x:allobjects[q].xtype.x,
y:allobjects[q].xtype.y,
userid:q,
}// array end
socket.broadcast.emit('newClientship',pack[q]);
} // else end
} // For ebf.
},1000/25); // Set Interval End
Client
var ship = Array();
var bullet = Array();
socket.on('newClientship',function(data){
ship[data.userid]= data;
});
socket.on('newBullet',function(data){
bullet= data;
});
var previous; // var for renderworld
renderWorld = function(timestamp){
//setInterval(function(){
if (!previous) previous = timestamp;
elapsed = timestamp - previous;
updateFps();
ctx.font="150px Arial";
ctx.fillStyle = "white";
ctx.clearRect(0,0,canvas.width,canvas.height);
for ( var i in ship){ ctx.fillText(ship[i].userid,ship[i].x,ship[i].y);
}
if (bullet.length > 10)
{
alert("ted");
}
for (var i in bullet){
ctx.fillText("X",100,100);
}
drawFps(200,20) ;
previous = timestamp;
window.requestAnimationFrame(renderWorld);
//},1000/25);
}
Just found out that the for loop on Server side doesn’t work
It gives me a „undefined“ if I console.log (allbullets.length)
If I do a console.log(allbullets) it shows me that it exists .
Bulletobj {
user_id: 47,
type: 'Bullet',
radius: 2,
basespeed: 1,
speed: 1,
velX: 0.2979165941720021,
velY: 0.9545919038609927,
targetX: 863,
targetY: 2429,
xtype: Obj { x: 153, y: 154, radius: 3, selected: 0 },
angleDeg: 1.268286927738952,
realAngle: 72.66748817105554 }
Now found a formula to count the items of an obj
console.log("length:"+Object.keys(allbullets).length);
But even that only counts the items in one of the objects and always shows 12
I wanted to have the count of all bullets that have an instance
FYI My ship has a procedure that creates a new bullet
ClientObj.prototype.fire =function (x,y){
allbullets = new Bulletobj(this.xtype.x,this.xtype.y,x,y,1,1, this.user_id);
}
// had put this var outside outside functions for global
allbullets = Array();
App.js
var allbullets = [];
Bulletobj = function(x,y,targetX,targetY,shipid,speed,user_id){
this.user_id = user_id;
this.type = 'Bullet';
this.radius = 2;
this.basespeed = speed;
this.speed = 4;
this.velX = 0;
this.velY = 0;
this.targetX = targetX;
this.targetY = targetY;
this.xtype = new Obj(x,y,3);
w.objects.push(this);
Bulletobj.prototype.update =function (){
tx = this.targetX - this.xtype.x;
ty = this.targetY - this.xtype.y;
dist = Math.sqrt(tx * tx + ty * ty);
this.angleDeg = Math.atan2(ty,tx) ;
this.realAngle = (Math.atan2(ty,tx) * 180/Math.PI + 360 ) % 360 ;
this.velX = (tx / dist) * this.speed ;
this.velY = (ty / dist) * this.speed ;
if (parseInt(dist) > parseInt(this.radius / 2)) {
this.xtype.x += parseInt(this.velX);
this.xtype.y += parseInt(this.velY);
} // if distance schleife end
} // Bulletobj update end
} // Bulletobj end
setInterval(function(){
var pack = Array();
var packbul = Array();
var packbularray = Array();
var spliceArray = Array();
objcnt = w.objects.length;
var i=0;
while (i < objcnt)
{
w.objects[i].update();
if(w.objects[i].hitpoints <= 0 )
spliceArray.push(i);
i++;
} // endwhile
for(var k = 0;k<spliceArray.length;k++)
{
w.objects.splice(spliceArray[k],1);
}
for(var q in SOCKET_LIST){
var socket = SOCKET_LIST[q];
if (allbullets === undefined)
{
}
else {
for(var k = 0;k<allbullets.length;k++)
{
if(allbullets[k].user_id == q)
{
packbul= {
x:allbullets[k].xtype.x,
y:allbullets[k].xtype.y,
userid:allbullets[k].user_id,
}// array end
} // if end
//obj end
} // end else undefined objects
} //end for
console.log(packbul);
socket.emit('newBullet',packbul);
if (allobjects[q] === undefined)
{
}
else{
console.log("q:"+q);
pack[q] = {
x:allobjects[q].xtype.x,
y:allobjects[q].xtype.y,
userid:q,
}// array end
socket.broadcast.emit('newClientship',pack[q]);
} // else end
} // For socket
},1000/25); // Set Interval End
Index.html
var ship = Array();
var bullet= Array();
var bulletdata =Array();
socket.on('newClientship',function(data){
ship[data.userid]= data;
});
socket.on('newBullet',function(data){
;
bulletdata.push(data);
});
var previous; // var for renderworld
renderWorld = function(timestamp){
if (!previous) previous = timestamp;
elapsed = timestamp - previous;
updateFps();
ctx.font="150px Arial";
ctx.fillStyle = "white";
ctx.clearRect(0,0,canvas.width,canvas.height);
for ( var i in ship){ ctx.fillText(ship[i].userid,ship[i].x,ship[i].y);
}
for (var i in bulletdata){
ctx.fillText(".",bulletdata[i].x,bulletdata[i].y);
}
bulletdata = [];
drawFps(200,20) ;
previous = timestamp;
window.requestAnimationFrame(renderWorld);
}
So I managed to get one bullet over to the client. Problem now is that each time a player shoots a bullet the old bullet vanish and the new start from beginning same with another client . 1 st client shoots . If the second client starts shooting the bullet from player 1 is deleted. Normally I wanted to have each client with his own bullets and all bullets that are created as objects on Server side to be drawn on the clients.
The ships work perfect but somehow the bullet flickers and doesn’t fit into the canvas 😔
Thank you
Solved !!! it after 3 frustrating days omg
again thx for the push without it I would have been lost
The problem was mixing object array and standard array.
E.g var x = {} —> Object Array
var x = []. —> Standard Array
People keep saying that you shouldn’t emit objects but atm I am glad the bullets arrived
at client . Also with standard arrays I would need to split after a choosen index and have to set the dataset points by myself . With object arrays it is easier to access them.
Maybe somebody can tell me what I emit when I push an object array into a standard array at the Server side . Is this an object or standard array ? Are the object headers that create performance issues (this is what in read somewhere) included in that array I emit to the client?
I mean on the client I side I receive an array but in the for loop with the ctx I have an object array again which was inside the standard array
if(allbullets[k].user_id == q)
{
packbul= {
x:allbullets[k].xtype.x,
y:allbullets[k].xtype.y,
userid:allbullets[k].user_id,
}// array end
packbularray.push(packbul);
} // if end
Index.html
var ship = Array();
var bullet= Array();
var bulletdata =Array();
socket.on('newClientship',function(data){
ship[data.userid]= data;
//ship.x = data.x;
//ship.y = data.y;
//ship.user = data.userid;
});
socket.on('newBullet',function(data){
bullet= data;
//bulletdata.push(bullet);
//ship.x = data.x;
//ship.y = data.y;
//ship.user = data.userid;
});
I have a rather broad question, but no idea how to tackle that. So forgive me.
I am trying to have several (like 200 and more) objects and, let's just say, a container to the side of the field, where I draw the objects. Now what I want is, that each object has some non visual attributes and when I click on that object, the attributes should appear in that container.
Now I could go about it?
I mean, I know I can ask for the name of the selected object and then do a key value query from some dictionary. Question is, whether there is an easier way to go about it.
For the click event I used a library called threex.domevents, check the GitHub page for more information, the code for the event it's self explanatory.
First domevents needs to be initialized in your scene like this:
var domEvents = new THREEx.DomEvents(camera, renderer.domElement);
Then I created a custom Mesh object:
// random id
function genRandomId()
{
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for( var i=0; i < 5; i++ )
text += possible.charAt(Math.floor(Math.random() * possible.length));
return text;
}
// random int for position
var min = -50;
var max = 50;
function genRandomInt(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
// custom mesh --------------------------------------------
function MyMesh(geometry, material, destinationContainer) {
THREE.Mesh.call(this, geometry, material);
this.userData = {
foo1: genRandomId(),
foo2: genRandomId(),
foo3: genRandomId(),
};
this.position.x = genRandomInt(min, max);
this.position.y = genRandomInt(min, max);
this.position.z = genRandomInt(min, max);
var that = this;
// click event listener
domEvents.addEventListener(this, 'click', function(event) {
console.log('clicked object on position:');
console.log(that.position);
destinationContainer.userData = that.userData;
console.log('Now the conainer has:');
console.log(destinationContainer.userData);
destinationContainer.userData = that.userData;
}, false);
}
MyMesh.prototype = Object.create(THREE.Mesh.prototype);
MyMesh.prototype.constructor = MyMesh;
genRandomId and genRandomInt are random generators for the pourpose of illustrating this example, I took the code for the random ids from Generate random string/characters in JavaScript.
In your scene you can generate 200 (or more) MyMesh meshes and add them to the scene:
const color = 0x156289;
const emissive = 0x072534;
var planeGeometry = new THREE.PlaneGeometry(5, 5);
var planeMaterial = new THREE.MeshPhongMaterial({
color: color,
emissive: emissive,
side: THREE.DoubleSide,
shading: THREE.FlatShading
});
var planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);
scene.add(planeMesh);
var objGeometry = new THREE.BoxGeometry(1, 1, 1);
var objMaterial = new THREE.MeshPhongMaterial({
color: color,
emissive: emissive,
shading: THREE.FlatShading
});
var i = 0;
while (i < 200) {
scene.add(new MyMesh(objGeometry, objMaterial, planeMesh));
i++;
}
And finally render the scene:
var render = function() {
requestAnimationFrame(render);
planeMesh.rotation.x += 0.010;
planeMesh.rotation.y += 0.010;
renderer.render(scene, camera);
};
render();
This is a demo with the full source code: http://run.plnkr.co/plunks/W4x8XsXVroOaLUCSeXgO/
Open the browser console and click on a cube and you'll see the that planeMesh is switching its userData attributes with the ones of the clicked cube mesh.
Yes, that's fine. You can put your own custom keys directly on a Three.js object and it shouldn't bother it as long as you don't accidentally overwrite an important built-in Three.js key. For that reason I'd recommend that you put all of your custom keys in a "namespace" on the object so they're nice and neat and contained.
For example, if you had a Three.js object foo, you could put all your keys under foo.myCustomNamespace, so that your custom data like foo.myCustomNamespace.name, foo.myCustomNamespace.description, etc. are all together and won't interfere with THREE.js properties.
Edit: Three.js provides a built-in namespace for user data called, conveniently, userData. Access it on THREE.Object3D.userData.
https://github.com/mrdoob/three.js/blob/master/src/core/Object3D.js#L92
The problem:
Bounding box volume is smaller than volume calculated from mesh.
What I've tried:
First I calculate the volume of a bounding box with the following code:
//loaded .obj mesh object:
var sizer = new THREE.Box3().setFromObject(obj);
console.log(sizer.size().x*sizer.size().z*sizer.size().y);
log output: 197112.65382983384
Then I calculate the volume of the mesh using this solution with this solution with the legacy geometry object in the method "calculateVolume" included below:
console.log(scope.calculateVolume(child));
calculateVolume is part of a class. My class methods are as follows:
threeDeeAsset.prototype.signedVolumeOfTriangle = function(p1, p2, p3) {
var v321 = p3.x*p2.y*p1.z;
var v231 = p2.x*p3.y*p1.z;
var v312 = p3.x*p1.y*p2.z;
var v132 = p1.x*p3.y*p2.z;
var v213 = p2.x*p1.y*p3.z;
var v123 = p1.x*p2.y*p3.z;
return (-v321 + v231 + v312 - v132 - v213 + v123)/6;
}
threeDeeAsset.prototype.calculateVolume = function(object){
var volumes = 0;
object.legacy_geometry = new THREE.Geometry().fromBufferGeometry(object.geometry);
for(var i = 0; i < object.legacy_geometry.faces.length; i++){
var Pi = object.legacy_geometry.faces[i].a;
var Qi = object.legacy_geometry.faces[i].b;
var Ri = object.legacy_geometry.faces[i].c;
var P = new THREE.Vector3(object.legacy_geometry.vertices[Pi].x, object.legacy_geometry.vertices[Pi].y, object.legacy_geometry.vertices[Pi].z);
var Q = new THREE.Vector3(object.legacy_geometry.vertices[Qi].x, object.legacy_geometry.vertices[Qi].y, object.legacy_geometry.vertices[Qi].z);
var R = new THREE.Vector3(object.legacy_geometry.vertices[Ri].x, object.legacy_geometry.vertices[Ri].y, object.legacy_geometry.vertices[Ri].z);
volumes += this.signedVolumeOfTriangle(P, Q, R);
}
return Math.abs(volumes);
}
log output: 336896.1562770668
Checking the source of the vertexes:
I also tried buffergeometry and of course, it's really the same array, but a typed Float32Array and predictably gave the same result:
var vol = 0;
scope.mesh.traverse(function (child) {
if (child instanceof THREE.Mesh) {
var positions = child.geometry.getAttribute("position").array;
for(var i=0;i<positions.length; i+=9){
var t1 = {};
t1.x = positions[i+0];
t1.y = positions[i+1];
t1.z = positions[i+2];
var t2 = {};
t2.x = positions[i+3];
t2.y = positions[i+4];
t2.z = positions[i+5];
var t3 = {};
t3.x = positions[i+6];
t3.y = positions[i+7];
t3.z = positions[i+8];
//console.log(t3);
vol += scope.signedVolumeOfTriangle(t1,t2,t3);
}
}
});
console.log(vol);
log output: 336896.1562770668
The question: Why is my bounding box volume smaller than the calculated volume of a closed mesh?
Perhaps I missing something such as an offset position or maybe I am calculating the bounding box volume wrong. I did several searches on google and stack, which is how I came to find the signedVolumeOfTriangle function above. it seems to be the most common accepted approach.
Could be a problem with winding order, you could try negating the result from signed volume, or reordering arguments.
The winding order will be clockwise or counterclockwise and determines the facing (normal) of the polygon,
volumes += this.signedVolumeOfTriangle(P, Q, R);
Swapping P and R inverts the normal,
volumes += this.signedVolumeOfTriangle(R, Q, P);
This can be complicated by storage techniques like triangle strips, where vertices are shared by adjacent triangles, causing winding order to alternate.
Another problem could be - especially if it works correctly for simple meshes - is degenerate vertices. If you're getting your meshes from an editor, and the mesh has been modified and tweaked a million times (usually the case), it's almost guaranteed to have degenerates.
There may be an option to weld close vertices in the editor that can help, or test with a known good mesh e.g. the Stanford bunny.
For some reason, and I'm not sure why, my Processing sketch doesn't display in JavaScript mode. However, in Java mode it runs how it's intended. I'm pretty sure that everything used is in the Processing API, so I really have no idea why this doesn't work.
A rundown on the program. Basically, I had to create a Stock Ticker with text moving from bottom right of the screen to the left on a constant loop.
Stock Ticker:
Stock[] quotes = new Stock[12];
float t=0;
PFont text;
void setup() {
size(400,200);
//Importing "Times" font
text = createFont( "Times" ,18,true);
//Assigning Strings and Float values to Stock array index
quotes[0] = new Stock("ADBE",68.60);
quotes[1] = new Stock("AAPL",529.36);
quotes[2] = new Stock("ADSK",51.41);
quotes[3] = new Stock("CSCO",21.87);
quotes[4] = new Stock("EA",30.17);
quotes[5] = new Stock("FB",67.07);
quotes[6] = new Stock("GOOG",1201.52);
quotes[7] = new Stock("HPQ",31.78);
quotes[8] = new Stock("INTC",25.41);
quotes[9] = new Stock("MSFT",40.49);
quotes[10] = new Stock("NVDA",18.56);
quotes[11] = new Stock("YHOO",38.24);
//Assigning Position of indexs using setX method from Stock class
float x = 0;
for (int i = 0; i < quotes.length; i++)
{
quotes[i].getX(x);
x = x + (quotes[i].width());
}
t = x;
}
void draw() {
background(55);
//Rendering of Text using trans and show functions from Stock class
for (int i = 0; i < quotes.length; i++)
{
quotes[i].trans();
quotes[i].show();
}
//Transparent Rectangl;e
noStroke();
fill(10,10,10,127);
rect(0,164,width,35);
}
Stock:
class Stock {
String stockName;
float stockNum;
float x;
String show;
Stock(String n, float v)
{
stockName = n;
stockNum = v;
show = (stockName + " " + stockNum + " ");
}
//Sets position of each index
void getX(float x_)
{
x = x_;
}
//Moves text
void trans()
{
x = x - 1;
if (x < width-t)
{
x = width;
}
}
//Renders Text
void show()
{
textFont(text);
textAlign(LEFT);
fill(255);
text(show,x,height-10);
}
//Records and returns width of index
float width()
{
textFont(text);
return textWidth(show);
}
}
Two things:
According to Processing.js page, it does not work with most Java, so
you need to be careful with what libraries you use. From their
Quick Start page:
Processing.js is compatible with Processing, but is not, and will
never be, fully compatible with Java. If your sketch uses functions or
classes not defined as part of Processing, they are unlikely to work
with Processing.js. Similarly, libraries that are written for
Processing, which are written in Java instead of Processing, will most
likely not work.
Even if the libraries you use are supported, you have to be very
careful while naming your variables because Javascript being a
typeless language can result in variable name clashes that you
wouldn't normally worry about when writing in Processing in Java
mode (or in Java directly) since it is a typed language. See
Processing.js's note on this.
Is it possible to load a JSON model once and add it to the scene multiple times with different scales, positions, etc?
If I add the Object3D() to an array, give a position and scale to the object in the array, add it to the scene, and then repeat this process, the position and scale are overwritten for every object in the array.
I can't think of anything that works, so I'm hoping someone could give me a working example of what I'm trying to accomplish.
Here's (one of many) of my failed attempts. Should give you a basic idea of what I'm trying to do, if my explanation wasn't sufficient.
function addModels(){
var models = [];
var model = new THREE.Object3D();
var modelTex = THREE.ImageUtils.loadTexture( "textures/model.jpg" );
var loader = new THREE.JSONLoader();
loader.load( "models/model.js", function( geometry ) {
mesh = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { map: modelTex }) );
model.add(mesh);
} );
for(var i = 0; i < 5; i++){
model.scale.set(i,i,i);
model.position.set(i,i,i);
models[i] = model;
scene.add(models[i]);
}
}
You need to clone a model first and then set position and scale.
for(var i = 0; i < 5; i++){
var newModel = model.clone();
newModel.position.set(i,i,i);
newModel.scale.set(i,i,i);
scene.add(newModel);
}
Updated: Example how you can create json model without load : Fiddle example or just simple add loop inside load function.
You can create new meshes from the same geometries and materials:
loader.load( "models/model.js", function( geometry ) {
var mat = new THREE.MeshLambertMaterial( { map: modelTex });
for (var i = 0; i < 5; i++) {
var mesh = new THREE.Mesh( geometry, mat );
mesh.position.set(i, i, i);
mesh.scale.set(i, i, i);
scene.add(mesh);
}
});