I'm trying to position a PolygonGeometry in the air in Cesium. In short, I'd like to use height to create an offset from the ground, and extrudedHeight to give the object a certain thickness. However when I set extrudedHeight, the height setting itself is ignored and the extrusion goes down all the way to the ground. So I can layer planes on top of each other, but no three-dimensional objects. What's the correct way to achieve this?
Here's what I'm doing so far:
polygonGeometry = Cesium.PolygonGeometry.fromPositions(
positions: pos,
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
extrudedHeight: #options.extrudedHeight,
height:#options.height
)
geometryInstance = new Cesium.GeometryInstance
geometry: polygonGeometry
primitive = new Cesium.Primitive
geometryInstances: [geoInstance]
It's hard to say exactly what the problem is, since your example is incomplete and also seems to be using a form of data binding that I'm not familiar with. But here's a complete example that can be copied/pasted into Sandcastle so that you can compare.
var viewer = new Cesium.Viewer('cesiumContainer');
var scene = viewer.scene;
var positions = Cesium.Cartesian3.fromDegreesArray([
-88.0, 35.0,
-80.0, 35.0,
-80.0, 40.0,
-88.0, 40.0
]);
var geometryInstance = new Cesium.GeometryInstance({
geometry : Cesium.PolygonGeometry.fromPositions({
positions : positions,
height: 1000000,
extrudedHeight: 1500000,
vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
}),
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.ORANGE)
}
});
scene.primitives.add(new Cesium.Primitive({
geometryInstances : geometryInstance,
appearance : new Cesium.PerInstanceColorAppearance({
closed : true,
translucent : false
})
}));
Related
I have a situation where I'm using Cesium.js entities to draw bearing lines from a point. In addition, I'd like to have a label attached to these bearing lines on the entity to show what it is. This is easy enough to do by attaching a label to the polyline entity. So far what I'm doing is creating a "really long" line along the bearing from the reference point and using the midpoint along that line as an anchor for the label (in Typescript):
let newEntity : Cesium.Entity;
let gccalc : gc.GreatCircle = new gc.GreatCircle();
let bearing : number = 45.0; //Bearing for the line
//this.currentPos is the lat/lon for the reference point for our bearing line
//gccalc is a simple class for computing great circle lines and has been omitted here (it is not relevant to the problem)
let destination = gccalc.destination(this.currentPos[0], this.currentPos[1], bearing, 1500, 'MI');
let anchorLabel = gccalc.destination(this.currentPos[0], this.currentPos[1], bearing, 50, 'MI');
const lineMat = new Cesium.PolylineDashMaterialProperty({
color : this.typeColorMap.get(contact.type)
});
const poses = Cesium.Cartesian3.fromDegreesArrayHeights([this.currentPos[1], this.currentPos[0], 500,
destination[1], destination[0], 500]); //500 is an arbitrary altitude for aesthetics
const nameString : string = "StringLabel";
let lineLabel = {
text: nameString,
font: '16px Helvetica',
fillColor : this.typeColorMap.get(contact.type),
outlineColor : Cesium.Color.BLACK,
outlineWidth : 2,
verticalOrigin : Cesium.VerticalOrigin.MIDDLE,
horizontalOrigin : Cesium.HorizontalOrigin.MIDDLE,
pixelOffset : new Cesium.Cartesian2(20, 0),
//pixelOffsetScaleByDistance : new Cesium.NearFarScalar(1.5e-1, 30.0, 1.5e7, 0.5)
};
let bearingLine = new Cesium.PolylineGraphics();
bearingLine.positions = poses;
bearingLine.width = 4;
bearingLine.material = lineMat;
bearingLine.arcType = Cesium.ArcType.NONE;
const lineEntity = {
name : 'Bearing Line',
polyline : bearingLine,
position : Cesium.Cartesian3.fromDegrees(anchorLabel[1], anchorLabel[0]),
show : true,
label : new Cesium.LabelGraphics(
lineLabel,
) as Cesium.LabelGraphics,
};
newEntity = new Cesium.Entity(lineEntity);
But the problem is the label is in geodesic (lat/lon) coordinates and does not stay on the screen as the user zooms in and out on the bearing line. So I also attempted to scale the position using the pixelOffsetScaleByDistance property, but this also doesn't work very well, and doesn't keep the label near the line
under 3d rotations (because the X and Y scaling would technically have to change).
It seems what I really need are the screen-space positions of the line endpoints, and a way to create the entity label at that midpoint. Is there a way to do this? If not, what is the best way to ensure that my label is always near my polyline regardless of user interactions with the Cesium map (such as zooms and rotations)?
To give an idea of what I'm trying to do, here's a screencap of Cesium with the labels as implemented. They look correct here, but only because I've made sure the zoom level and rotation is correct:
this is the link of my work : maison.whiteplay.fr
What i am trying to do is a 3D PacMan, but look at th code, i'm using mesh to build my level, except for bubbles (yellow circle), that you need to eat to win.
They are all different objects, but because they are a lot, it's lagging, can i use the same technologie (mesh i think) to the bubbles ? If yes, how ?
code:
var geometrySphere = new THREE.SphereGeometry( 5, 32, 32 );
var bille = function(x,z){
this.bille = new THREE.Mesh(
geometrySphere,
new THREE.MeshBasicMaterial( {color: 0xffff00} )
);
this.bille.position.x = (x-15.5)*100; this.bille.position.y = 100;
this.bille.position.z = (z-15.5)*100; scene.add(this.bille);
}
Thank your for reading, and if you have any suggestions about my code, don't hesistate :D
You can also reuse your material instead of making a new instance all the time:
var geometrySphere = new THREE.SphereGeometry( 5, 32, 32 );
var billeMaterial = new THREE.MeshBasicMaterial( {color: 0xffff00} );
var bille = function(x,z){
this.bille = new THREE.Mesh(
geometrySphere,
billeMaterial
);
this.bille.position.x = (x-15.5)*100; this.bille.position.y = 100;
this.bille.position.z = (z-15.5)*100; scene.add(this.bille);
}
Reusing materials has good influence on performance.
How do you duplicate your meshes/objects?
You're code is almost right.
In your specific case, with N equal balls, you must have N mesh but just one material.
In this way, if you want to colorize (just for example) only one ball, you have to apply it one new material, otherwise you will apply new color to all the balls.
In your case lagging may be due to the sphere construction.
You clearly copy and paste from the documentation without read it before.
var geometrySphere = new THREE.SphereGeometry( 5, 32, 32 );
Where, as explained in the documentation:
radius — sphere radius. Default is 50.
widthSegments — number of horizontal segments. Minimum value is 3, and the default is 8.
heightSegments — number of vertical segments. Minimum value is 2, and the default is 6.
32 * 32 is too much for your small-monocolor bubbles, doesn't have sense.
The higher are this values, the higher is the complexity for draw it for each frame.
I suggest you to create sphere with a minor number of vertical/horizontal segments (8*8 may be ok).
Take a look at this demo.
I'm trying to change the length of a cylinder or the extrudedHeight of a circle when it has been added to the primitives and is shown in the cesium widget/viewer. For example this cylinder:
var length = 100;
var cylinderGeometry = new Cesium.CylinderGeometry({
length : length,
topRadius : cylinderradius,
bottomRadius : cylinderradius,
slices: cylinderslices,
vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
});
var cylinder = new Cesium.GeometryInstance({
geometry: cylinderGeometry,
modelMatrix: Cesium.Matrix4.multiplyByTranslation(
Cesium.Transforms.eastNorthUpToFixedFrame(ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(lon, lat))),
new Cesium.Cartesian3(0.0, 0.0, length * 0.5)),
attributes: {
color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED)
},
id: "Cylinder1"
});
var primitive = new Cesium.Primitive({
geometryInstances : cylinder ,
appearance : new Cesium.PerInstanceColorAppearance({
closed : false,
translucent: true,
flat: false,
faceForward: true
}),
allow3DOnly: true,
vertexCacheOptimize: true,
allowPicking: true,
releaseGeometryInstances: false
});
widget.scene.primitives.add(primitive);
Because it's added to the primitives array it will be shown in the widget, but after 2 seconds for example I get a notification that the length should be halved (that means set to 50). Is there any way to do this? Simply changing it in cylinderGeometry doesn't seem to do the job.
I kind of have it working by creating a new cylinder with the new height, adding it and removing the old one. This however tends to flicker the cylinder (it's gone for a fraction of a second) before the new one is shown. I fixed this problem by removing the old instance after a set time after the new one is added. This whole solution isn't very elegant and doesn't work very well on devices with a small amount of computing power, hence my search for a better solution.
I don't care if this is achieved using cylinders or extruded circles. If you need any more information don't hesitate to ask in the comments below the question.
EDIT
I implemented the second solution Matthew suggested but after a while of it running perfectly the cylinders stop changing height (which didn't occur when I used my solution. The callback in the interval does get called. Here is some code showing what my new solution is (not working):
primitives.add(prim);
window.nodeValuesInterval = setInterval(function () {
if (prim._state == Cesium.PrimitiveState.COMPLETE) {
clearInterval(window.nodeValuesInterval);
clearTimeout(window.nodeValuesTimeout);
primitives.remove(primitiveObjects.value);
primitiveObjects.value = prim;
}
}, cylindervalueinterval);
window.nodeValuesTimeout = setTimeout(function () {
clearInterval(window.nodeValuesInterval);
primitives.remove(primitiveObjects.value);
primitiveObjects.value = prim;
}, cylindervaluedelay);
Cesium's geometry is currently optimized for static data. Some attributes, such as visibility, color, and material can be changed on the fly, but items that actually modify the geometry (like cylinder height) require you to remove the primitive and recompute the geometry. The flickering your seeing is the result of asynchronous primitive creation being on by default. There are two ways to do want you want.
Disable asynchronous primitive create by passing [options.asynchronous: false to the Primitive constructor. This means that when you add a new primitive, Cesium will not render until it is ready. For one or two objects, you won't notice anything. For lots of objects it will lock up the browser until everything is ready. This does guarantee that you can remove old/add new primitives without any flicker.
The second option is to add your new primitive (without removing the old one) and then every frame, check the _state property of your new Primitive (I thought this was part of the public API but apparently it's not). When the _state is equal to Cesium.PrimitiveState.COMPLETE you can safely remove the old primitive and your guaranteed the new one will render (hence no flicker).
I think we have a bug/feature request to expose the state variable publicly or otherwise notify when the Primitive is ready; but using _state should be fine for the forseeable future. I'll update this issue if we add an official way sometime soon.
Hope that helps.
EDIT: Since more help was requested; here's a complete example. You can copy and paste the below code into Sandcastle using this link.
Basically it uses the scene.preRender event instead of a timeout (preRender is almost always the better answer here). Also, if you receive a new update before the old one is finished processing, it's important to remove that one before computing the new one. Let me know if you are still having problems.
require(['Cesium'], function(Cesium) {
"use strict";
var widget = new Cesium.CesiumWidget('cesiumContainer');
var ellipsoid = widget.scene.globe.ellipsoid;
var lon = 0;
var lat = 0;
var cylinderradius = 30000;
var length = 10000000;
var cylinderslices = 32;
var newPrimitive;
var currentPrimitive;
//This function creates a new cylinder that is half the length of the old one.
function decreaseLength() {
//If there's a pending primitive already, remove it.
if(Cesium.defined(newPrimitive)){
widget.scene.primitives.remove(newPrimitive);
}
length /= 2;
var cylinderGeometry = new Cesium.CylinderGeometry({
length : length,
topRadius : cylinderradius,
bottomRadius : cylinderradius,
slices: cylinderslices,
vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
});
var cylinder = new Cesium.GeometryInstance({
geometry: cylinderGeometry,
modelMatrix: Cesium.Matrix4.multiplyByTranslation(Cesium.Transforms.eastNorthUpToFixedFrame(ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(lon, lat))),
new Cesium.Cartesian3(0.0, 0.0, length * 0.5)),
attributes: {
color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED)
},
id: "Cylinder1"
});
newPrimitive = new Cesium.Primitive({
geometryInstances : cylinder ,
appearance : new Cesium.PerInstanceColorAppearance({
closed : false,
translucent: true,
flat: false,
faceForward: true
}),
allow3DOnly: true,
vertexCacheOptimize: true,
allowPicking: true,
releaseGeometryInstances: false
});
//We add the new cylinder immediately, but don't remove the old one yet.
widget.scene.primitives.add(newPrimitive);
}
//Create the initial cylinder.
decreaseLength();
//Subscribe to the preRender event so we can check the primitive every frame.
widget.scene.preRender.addEventListener(function(scene, time) {
//Remove the old cylinder once the new one is ready.
if(Cesium.defined(newPrimitive) && newPrimitive._state === Cesium.PrimitiveState.COMPLETE){
if(Cesium.defined(currentPrimitive)){
widget.scene.primitives.remove(currentPrimitive);
}
currentPrimitive = newPrimitive;
newPrimitive = undefined;
}
});
Sandcastle.addToolbarButton('Decrease Length', decreaseLength);
Sandcastle.finishedLoading();
});
I have a system of tubes. More precisely, I create a tubes from this code
tube_color - obviously, color of tube,
spline_points - huge number of THREE.Vector3 objects,
segments, radiusSegments are just numbers
var material = new THREE.MeshLambertMaterial( { color: tube_color, shading: THREE.SmoothShading } );
var spline = new THREE.SplineCurve3(spline_points);
var tube = new THREE.TubeGeometry(spline, segments, 10, radiusSegments, false, false);
var tubeMesh = new THREE.Mesh(tube, material);
scene.add(tubeMesh);
This code creates one particular mesh object in space. For each mesh I can have an array of Vector3's by using myMesh.geometry.vertices.
The problem is: I have the point in 3d space. Around this point I create Cube, which does intersect with tubes. For example, this cube can be created like this
var cube = new THREE.CubeGeometry(xSize,ySize,zSize, 5, 5, 5);
var material = new THREE.MeshBasicMaterial({
color: 0xff0000,
opacity: 1,
wireframe: true,
transparent: false
});
var selectionMesh = new THREE.Mesh(cube, material);
scene.add(selectionMesh);
Is it possible at least to find objects(meshes) that have intersection with by cubic area? I know that in scene object I have all meshes, and I can iterate over them, get vertices, iterate over them with a condition if there is at least one its point is in cubic area. But I believe... I hope there is a method/algorithm/magic much more simple then this...
As #WestLangley recommended the solution of this problem was to build octree.
octree=new THREE.Octree({
undeferred:false,
depthMax:Infinity,
objectsThreshold:8,
overlapPct:0.15
} );
and search it
var meshesSearch=octree.search( rayCaster.ray.origin, radiusSphere, true, rayCaster.ray.direction );
However, to have specific intersection we need to provide the recursive flag
raycaster.intersectOctreeObjects( objects, true )
I ended up with more complicated processing of my specific case however this was enough to solve the problem at that moment.
I'd like to make a minimap of my rpg game.
Is making a minimap as simple as dividing all object dimensions, velocities, and coordinates by however large you want the minimap?
For example below... You have a size of 1000x1000px, a canvas (viewport) of 500x500px, the player is located in the center of the viewport... If you wanted a minimap half the size of the actual world, you would do:
Player/Viewport x,y velocity/2
Player/Viewport x,y coordinates/2
Canvas, world, and all objects' width and height are divided by 2
etc...
That way the rendering of the minimap on the world and the velocities are scaled accurately? Am I missing anything?
Thanks!
EDIT: Something like this?
function miniMap() {
$(".minimapHolder").show();
$("#mini_map").text("hide minimap");
var minicanvas = document.getElementById("miniMap");
ministage = new createjs.Stage("miniMap");
minicam = new createjs.Shape();
minicam.graphics.beginStroke("white").drawRoundRect(0, 0, 100, 40, 5);
//blip representation of Player
player_blip = new createjs.Shape();
player_blip.graphics.beginFill("yellow").drawRoundRect(0, 0, 11.2, 12, 1);
animal_blip = new createjs.Shape();
animal_blip.graphics.beginFill("red").drawRoundRect(0, 0, 24.4, 21.6, 1);
player_blip.x = players_Array[0].x/5;
player_blip.y = players_Array[0].y/5;
animal_blip.x = animalContainer.x/5;
animal_blip.y = animalContainer.y/5;
minicam.x = players_Array[0].x-110;
minicam.y = players_Array[0].y-110;
ministage.addChild(player_blip, animal_blip, minicam);
ministage.update();
}
function updateMiniMap() {
player_blip.x = players_Array[0].x/5;
player_blip.y = players_Array[0].y/5;
if (ContainerOfAnimals.children[0] != null) {
var pt = ContainerOfAnimals.localToGlobal(ContainerOfAnimals.children[0].x, ContainerOfAnimals.children[0].y);
console.log(pt.x);
animal_blip.x = pt.x/5;
animal_blip.y = pt.y/5;
} else {
ministage.removeChild(animal_blip);
}
minicam.x = player_blip.x-40;
minicam.y = player_blip.y-15;
ministage.update();
}
Gives:
Short anwswer: "It will(most likely) work." ... but:
What you are trying to achieve is just scaling the stage/container, so you could also just use a copy of everything and put it into a container and scale it down to 0.5, but that is not the purpose of a minimap.
Objects of the minimap should only be a representation of the object in the 'real' world and should therefore not have any velocity ect.(that should especially not be updated separately from the 'real' world) - while your approach will probably work, you'd allways have to keep track and update every property, this will get messy quickly or even lead to differences if you miss some tiny things.
A more 'clean'(and simple) approach to this would be, that each minimap-object has a reference to the object in the 'real' world and on each tick, it just reads the x/y-coordinates and updates its' own coordinates based on the minimap-scale.
Another thing is the graphics: Scaling-operations can be costly(performance wise), especially when they are done each frame, so IF you use the same graphics for the minimap you should at least used a cached DisplayObject and not have the graphics scaled each frame.