Trying to add shadow to entities using CesiumJS - cesiumjs

I'm trying to add a "shadow" of the in my replay but I'm not exactly sure if I should name that as a "shadow".
Please, look at my screenshot, you can see a red shadow below every entities (where I set the dots to highlight what I'm looking for)
You can see it live on https://ayvri.com/scene/z15y96gzjx/ck43xpxd500013e5ra7dh6s8e
I did try to find something documented for this feature, but so far, I didn't found. Neither on the Sandcastle.
I thought I would need to add particles to add a kind of tail, but I'm not even sure if it's the right direction to investigate on.
Thanks for any help!
Edit 2 : To see clearly what I need to reproduce, you can also check this screenshot. Below the white trace, you can see a red gradient (from opacity 0 to 1) starting from the beginning of the trace.

there is working example with ground tracer as I promised:
var viewer = new Cesium.Viewer('cesiumContainer', {
scene3DOnly : true,
shadows : true,
timeline: false,
terrainShadows : Cesium.ShadowMode.ENABLED
});
var terrainProvider = Cesium.createWorldTerrain();
viewer.terrainProvider = terrainProvider;
//---------------------- Start position of the plane ---------------------------
var startLongitude = -123.0744619, startLatitude = 44.0503706;
viewer.camera.flyTo({destination: Cesium.Cartesian3.fromDegrees(startLongitude, startLatitude, 900)});
//--------------------------- CREATE A PLANE ENTITY ----------------------------
var plane = viewer.entities.add({
// position: new Cesium.Cartesian3.fromDegrees(startLongitude, startLatitude, 600),
model : {
uri : '../SampleData/models/CesiumBalloon/CesiumBalloon.glb',
minimumPixelSize : 128,
maximumScale : 20000,
color : Cesium.Color.BLACK.withAlpha(0.5),
}
});
//------------ Array of (time, position) samples along the plane route ---------
var airPathArray = [];
//------------ Array of the points of the ground path under the plane ----------
var groundPathArray = [];
//------------ Initialize arrays ----------
var timeNow = new Cesium.JulianDate.now();
airPathArray.push({time: timeNow, degrees: {longitude: startLongitude, latitude: startLatitude},
position: new Cesium.Cartesian3.fromDegrees(startLongitude, startLatitude, 600)});
groundPathArray.push(startLongitude);
groundPathArray.push(startLatitude);
//----------- Every 1 second a new point on the plane route is created ---------
var pathHandler = setInterval(function(){
var timeNow = new Cesium.JulianDate.now();
//timeNow = Cesium.JulianDate.addSeconds(timeNow, 2, new Cesium.JulianDate());
// At start it takes initial coordinates
// New next point coordinates are randomly taken
var lon = airPathArray[airPathArray.length - 1].degrees.longitude + 0.0005 * (1 - Math.random());
var lat = airPathArray[airPathArray.length - 1].degrees.latitude + 0.0005 * (1 - Math.random());
airPathArray.push({time: timeNow, degrees: {longitude: lon, latitude: lat}, position: new Cesium.Cartesian3.fromDegrees(lon, lat, 600)});
groundPathArray.push(lon, lat);
// The maximum number of the route points is fixed
if(airPathArray.length > 3){
airPathArray.shift();
groundPathArray.shift();
groundPathArray.shift();
}
// The updated route is ready for visualization
viewRoute();
}, 1000);
// Route visualization
function viewRoute() {
// console.log(airPathArray[0].position);
var trace = new Cesium.SampledPositionProperty();
for(var i = 0; i < airPathArray.length; i++) {
trace.addSample(airPathArray[i].time, airPathArray[i].position);
}
plane.position = airPathArray[0].position;
//plane.position.setInterpolationOptions({
// interpolationDegree : 1,
// interpolationAlgorithm : Cesium.LinearApproximation
//});
plane.orientation = new Cesium.VelocityOrientationProperty(trace);
}
//------------------------ CREATE A GROUND PATH ENTITY -------------------------
function updatePositions() {
var positions = new Cesium.Cartesian3.fromDegreesArray(groundPathArray);
return positions;
}
var goroundPath = viewer.entities.add({
corridor : {
width : 50.0,
material : Cesium.Color.BLUE.withAlpha(0.5),
positions: new Cesium.CallbackProperty(updatePositions, false)
}
});

Related

How to smooth mesh triangles in STL loaded BufferGeometry

I´m trying to load some STL files using Three.js. The models are loaded correctly, but there are too many triangles that I would like to merge/smooth.
I had successfully applied smooth loading terrains in other 3D formats, but I can´t do it with the BufferGeometry that results from loading an STL file with the STLLoader.
_
var material = new THREE.MeshLambertMaterial( { ... } );
var path = "./models/budah.stl";
var loader = new THREE.STLLoader();
loader.load( path, function ( object ) {
object.computeBoundingBox();
object.computeBoundingSphere();
object.computeFaceNormals();
object.computeVertexNormals();
object.normalizeNormals();
object.center();
// Apply smooth
var modifier = new THREE.SubdivisionModifier( 1);
var smooth = smooth = object.clone();
smooth.mergeVertices();
smooth.computeFaceNormals();
smooth.computeVertexNormals();
modifier.modify( smooth );
scene.add( smooth );
});
This is what I tried, it throws an error: Uncaught TypeError: smooth.mergeVertices is not a function
If I comment the "mergeVertices()" line, what I get is a different error: Uncaught TypeError: Cannot read property 'length' of undefined in SubdivisionsModifier, line 156.
It seems that the sample codes I´m trying are outdated (this is happenning a lot recently due to the massive changes in the Three.JS library). Or maybe I´m forgetting something. The fact is that the vertices seems to be null..?
Thanks in advance!
It seems I was looking in the wrong direction: smoothing the triangles has nothing to do with the SubdivisionsModifier... What I needed was easier than that, just compute the vertex BEFORE applying the material, so it can use SmoothShading instead of FlatShading (did I got it right?).
The problem here was that the BufferGeometry returned by the STLLoader has not calculated vertices/vertex, so I had to do it manually. After that, apply mergeVertices() just before computeVertexNormals() and voilà! The triangles dissappear and everything is smooth:
var material = new THREE.MeshLambertMaterial( { ... } );
var path = "./models/budah.stl";
var loader = new THREE.STLLoader();
loader.load( path, function ( object ) {
object.computeBoundingBox();
object.computeVertexNormals();
object.center();
///////////////////////////////////////////////////////////////
var attrib = object.getAttribute('position');
if(attrib === undefined) {
throw new Error('a given BufferGeometry object must have a position attribute.');
}
var positions = attrib.array;
var vertices = [];
for(var i = 0, n = positions.length; i < n; i += 3) {
var x = positions[i];
var y = positions[i + 1];
var z = positions[i + 2];
vertices.push(new THREE.Vector3(x, y, z));
}
var faces = [];
for(var i = 0, n = vertices.length; i < n; i += 3) {
faces.push(new THREE.Face3(i, i + 1, i + 2));
}
var geometry = new THREE.Geometry();
geometry.vertices = vertices;
geometry.faces = faces;
geometry.computeFaceNormals();
geometry.mergeVertices()
geometry.computeVertexNormals();
///////////////////////////////////////////////////////////////
var mesh = new THREE.Mesh(geometry, material);
scene.add( mesh );
});
Than, you can convert it back to BufferGeometry, because it's more GPU/CPU efficient for more complex models:
var geometry = new THREE.Geometry();
geometry.vertices = vertices;
geometry.faces = faces;
geometry.computeFaceNormals();
geometry.mergeVertices();
geometry.computeVertexNormals();
var buffer_g = new THREE.BufferGeometry();
buffer_g.fromGeometry(geometry);
var mesh = new THREE.Mesh(buffer_g, material);
scene.add( mesh )
Happened this issue for me while loading an obj file. If you have a 3d software like 3dsmax:
Open the obj file,
Go to polygons selection mode and select all polygons.
Under the Surface properties panel, click 'Auto Smooth' button.
Export the model back to obj format
Now you won't have to call the functions geometry.mergeVertices() and geometry.computeVertexNormals();. Just load the obj and add to the scene, mesh will be smooth.
EDIT:
My obj files had meshphongmaterial by default and on changing the shading property to value 2 the mesh became smooth.
child.material.shading = 2
STL does not support vertex index.
That is reason it has duplicated vertex of all triangles.
Each vertex has its normal as triangle normal.
As a result, at same position( multiple very closed vertices), there is multiple normal value.
This leads to non-smooth surface of geometry when using Normal for lighting calculation.

Cocos2d-js: effect on cocos2d-js application running on browser while a move from one application to another application

i was running parkour game given on cocos2d website on my browser. everything was working fine, but when i move from my browser to sublime text and returned to my browser, the running player start to show some unexpected behaviour, the player disappeared from its position and and after few second it fall on the ground and then start running again .whenever i move from one application to another it happens.
i don't why its happening. could some one tell me how to prevent this from happen?
here is the code for animation layer:-
var AnimationLayer = cc.Layer.extend({
spriteSheet: null,
runningAction: null,
sprite: null,
space:null,
body:null,
shape:null,
ctor:function (space) {
this._super();
this.space = space;
this.init();
this._debugNode = cc.PhysicsDebugNode.create(this.space);
this._debugNode.setVisible(false);
// Parallax ratio and offset
this.addChild(this._debugNode, 10);
},
init:function () {
this._super();
// create sprite sheet
cc.spriteFrameCache.addSpriteFrames(res.runner_plist);
this.spriteSheet = cc.SpriteBatchNode.create(res.runner_png);
this.addChild(this.spriteSheet);
// init runningAction
var animFrames = [];
for (var i = 0; i < 8; i++) {
var str = "runner" + i + ".png";
var frame = cc.spriteFrameCache.getSpriteFrame(str);
animFrames.push(frame);
}
var animation = cc.Animation.create(animFrames, 0.1);
this.runningAction = cc.RepeatForever.create(cc.Animate.create(animation));
//create runner through physic engine
this.sprite = cc.PhysicsSprite.create("#runner0.png");
var contentSize = this.sprite.getContentSize();
// init body
this.body = new cp.Body(1, cp.momentForBox(1, contentSize.width, contentSize.height));
this.body.p = cc.p(g_runnerStartX, g_groundHight + contentSize.height / 2);
this.body.applyImpulse(cp.v(150, 0), cp.v(0, 0));//run speed
this.space.addBody(this.body);
//init shape
this.shape = new cp.BoxShape(this.body, contentSize.width - 14, contentSize.height);
this.space.addShape(this.shape);
this.sprite.setBody(this.body);
this.sprite.runAction(this.runningAction);
this.spriteSheet.addChild(this.sprite);
this.scheduleUpdate();
},
getEyeX:function () {
return this.sprite.getPositionX() - g_runnerStartX;
}});
and this is the code for playScene.js:-
var PlayScene = cc.Scene.extend({
space:null,
gameLayer:null,
// init space of chipmunk
initPhysics:function() {
this.space = new cp.Space();
// Gravity
this.space.gravity = cp.v(0, -350);
// set up Walls
var wallBottom = new cp.SegmentShape(this.space.staticBody,
cp.v(0, g_groundHight),// start point
cp.v(4294967295, g_groundHight),// MAX INT:4294967295
0);// thickness of wall
this.space.addStaticShape(wallBottom);
},
onEnter:function () {
this._super();
this.initPhysics();
this.gameLayer = cc.Layer.create();
//add three layer in the right order
this.gameLayer.addChild(new BackgroundLayer(), 0, TagOfLayer.background);
this.gameLayer.addChild(new AnimationLayer(this.space), 0, TagOfLayer.Animation);
this.addChild(this.gameLayer);
this.addChild(new StatusLayer(), 0, TagOfLayer.Status);
this.scheduleUpdate();
},
update:function (dt) {
// chipmunk step
this.space.step(dt);
var animationLayer = this.gameLayer.getChildByTag(TagOfLayer.Animation);
var eyeX = animationLayer.getEyeX();
this.gameLayer.setPosition(cc.p(-eyeX,0));
}
});
Ok, this may or may not solve the issue some scenarios, but after looking at it profoundly, the biggest suspect seems to be a lack of steps in the calculations of Chipmunk when the framerate drops (which appears to happen when you resize or minimize/maximize the screen).
The closest thing to a fix that I've found comes from a lead in this post in the forums, and which I've adapted it like this: where you have this.space.step(dt); in your update function, change it for the following:
var timeWindow = 1/60; //replace by your configured FPS rate if it's not 60
var steps = Math.ceil(dt / timeWindow);
var i = 0;
for (; i < steps; i++) {
this.space.step(timeWindow);
}
This should ensure that even if your application drops below 60fps the physics simulation will be updated properly, and this problem should not happen anymore.

Ye olde Pipe Flow Animation

i try to script a flow-animation ( basic color, nothing fancy ) through a grid of "pipes".
( think of a 5*5 tiled screen)
since the pipes are created completely dynamic at runtime, the animation has to be scripted also.
At the moment it escapes my mind on how to do this in actionscript, without pre-generated masks.
thanks for all hints!
You wish to visually 'simulate' the flow of some liquid (for instance water) through pipes in the same style that it is done in pipe games?
http://www.mclelun.com/img/blog/120411_pipe_02.jpg
Alright...
Are you willing to use bitmapData (pixel) to create this effect?
Here is how I would go about it..
create a short script to fill a rectangle (block) of pixels gradually..
ie
var animateOn : Boolean = true;
var startPoint : Point = new Point(beginX , beginY);
var endPoint : Point = new Point(finishX, finishY);
var step : Number = 1 / Point.Distance(startPoint, endPoint);
var currentPos : Number = 0;
onEnterFrame(e : Event):void
{
var p : point = Point.interpolate(startPoint, endPoint, currentPos);
bitmap.drawRect(p.x - 2, p.y - 2, 4, 4, someColor);
currentPos += step;
}
This is just an example of the top of my head (will not compile)
The idea is to keep feeding in the right startPoint and endPoint for each tile..
You could easily animate it like with without using any mask.
You can define each type of tile as a vector of points and then iterate from one point to another by adding the position of the tile to each point.
To fill a curve dynamically you may want to use this closed formula for a Bezier curve:
//start point
var s = new Point(x0, y0);
//cont point
var c = new Point(x1, y1);
//end point
var e = new Point(x2, y2);
var step : Number = 1 / (Point.distance(startPoint, controlPoint) + Point.distance(controlPoint, endPoint));
var t : Number = 0.0;
private function onEnterFrame(e : Event):void
{
var p : Point = new Point();
p.x = (s.x * (1-t) + c.x * t) * (1 - t) + (c.x * (1-t) + e.x * t) * t;
//do the same for y axis
drawSomething(p.x, p.y);
t+= step;
}
this will animate a curve style flow

Adding many circles to a google map

I'm attempting to draw many circles (around 1000) on a google map, at various locations and sizes, and then binding a click event to them. But this many calls of new google.maps.Circle(parameters); makes the page loading slow, and sometimes it just hangs forever, so I'd like to figure out if there's a better/faster way to accomplish what I'm trying to do.
I see that there's something called a kml layer, but there doesn't appear to be any easy way to draw filled circles with it and I'm not sure if I can still bind click events to each individual circle in the layer or not.
Looking at the circle workaround on the google KML faq page, I'm not sure if generating a KML file that contains thousands of circles similar to this would end up saving any time.
I also have no idea how to go about generating this kml file.
Finally, take into consideration that I'm pulling the circles I'm attempting to draw from a database, so I'd either have to generate the KML file on the fly for use or generate a new file every time a circle is removed or added from the DB so that the file stays up to date.
Of course, if there's another alternative, I'm all ears!
With the help of others via the Google Maps API v3 Group, I was able to implement a map overlay that handles 10,000 points suprisingly well. The trick is to use a canvas tile overlay, which minimizes the number of DOM elements in exchange for much simpler/lighter-weight POJsOs (plain old JavaScript objects).
Demo page w/mouse click events (API v2 only): http://notebook.kulchenko.com/maps/datamark
Demo page w/cursor swapping (API v2 and v3): http://notebook.kulchenko.com/maps/gridmark
Here is yet another example that demonstrates how to render multiple objects on Google Map using Overlay approach. Since the performance could degrade considerably while the amount of objects (e.g. google.maps.Circle) is increasing, it is proposed to render objects using canvas element instead of divone.
Example
The example demonstrates how to render 1k objects (cities)
var overlay;
USCitiesOverlay.prototype = new google.maps.OverlayView();
function USCitiesOverlay(map) {
this._map = map;
this._cities = [];
this._radius = 6;
this._container = document.createElement("div");
this._container.id = "citieslayer";
this.setMap(map);
this.addCity = function (lat, lng,population) {
this._cities.push({position: new google.maps.LatLng(lat,lng),population: population});
};
}
USCitiesOverlay.prototype.createCityIcon = function (id,pos,population) {
var cityIcon = document.createElement('canvas');
cityIcon.id = 'cityicon_' + id;
//calculate radius based on poulation
this._radius = population / 100000;
cityIcon.width = cityIcon.height = this._radius * 2;
cityIcon.style.width = cityIcon.width + 'px';
cityIcon.style.height = cityIcon.height + 'px';
cityIcon.style.left = (pos.x - this._radius) + 'px';
cityIcon.style.top = (pos.y - this._radius) + 'px';
cityIcon.style.position = "absolute";
var centerX = cityIcon.width / 2;
var centerY = cityIcon.height / 2;
var ctx = cityIcon.getContext('2d');
ctx.fillStyle = 'rgba(160,16,0,0.6)';
ctx.beginPath();
ctx.arc(centerX, centerY, this._radius, 0, Math.PI * 2, true);
ctx.fill();
return cityIcon;
};
USCitiesOverlay.prototype.ensureCityIcon = function (id,pos,population) {
var cityIcon = document.getElementById("cityicon_" + id);
if(cityIcon){
cityIcon.style.left = (pos.x - this._radius) + 'px';
cityIcon.style.top = (pos.y - this._radius) + 'px';
return cityIcon;
}
return this.createCityIcon(id,pos,population);
};
USCitiesOverlay.prototype.onAdd = function () {
var panes = this.getPanes();
panes.overlayLayer.appendChild(this._container);
};
USCitiesOverlay.prototype.draw = function () {
var zoom = this._map.getZoom();
var overlayProjection = this.getProjection();
var container = this._container;
this._cities.forEach(function(city,idx){
var xy = overlayProjection.fromLatLngToDivPixel(city.position);
var cityIcon = overlay.ensureCityIcon(idx,xy,city.population);
container.appendChild(cityIcon);
});
};
USCitiesOverlay.prototype.onRemove = function () {
this._container.parentNode.removeChild(this._container);
this._container = null;
};
function getRandomInterval(min, max) {
return Math.random() * (max - min) + min;
}
function generateCityMap(count) {
var citymap = [];
var minPos = new google.maps.LatLng(49.25, -123.1);
var maxPos = new google.maps.LatLng(34.052234, -74.005973);
for(var i = 0; i < count;i++)
{
var lat = getRandomInterval(minPos.lat(),maxPos.lat());
var lng = getRandomInterval(minPos.lng(),maxPos.lng());
var population = getRandomInterval(100000,1000000);
citymap.push({
location: new google.maps.LatLng(lat, lng),
population: population
});
}
return citymap;
}
function initialize() {
var mapOptions = {
zoom: 4,
center: new google.maps.LatLng(37.09024, -95.712891),
mapTypeId: google.maps.MapTypeId.TERRAIN
};
var map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
overlay = new USCitiesOverlay(map);
//overlay.addCity(40.714352, -74.005973); //chicago
//overlay.addCity(40.714352, -74.005973); //newyork
//overlay.addCity(34.052234, -118.243684); //losangeles
//overlay.addCity(49.25, -123.1); //vancouver
var citymap = generateCityMap(1000);
citymap.forEach(function(city){
overlay.addCity(city.location.lat(), city.location.lng(),city.population);
});
}
google.maps.event.addDomListener(window, 'load', initialize);
html, body, #map-canvas {
height: 100%;
margin: 0px;
padding: 0px;
}
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&signed_in=true"></script>
<div id="map-canvas"></div>
Forget about KML, custom tiles are the way to go.
Have a look at these county maps:
http://maps.forum.nu/v3/gm_customTiles.html (check the "Density" box).
and
http://maps.forum.nu/gm_main.html?lat=31.428663&lon=-110.830078&z=4&mType=10
(click on the map to get county info)
These maps have 3000+ polygons, (not circles), and they load fast. The first link is API V3, the second is API V2.
The second map (V2) has click events. The click event handler is attached to the map itself, and it sends an AJAX call to the server with the lat/lon of the click. The server side script then looks this lat/lon up in the database to determine which county was clicked.

Google Maps add 2 GEvent Listeners. 1 for each marker

I have the following code which lets the user plot two points on a Google MAP. I then want to be able to catch the event for each point(marker) being dragged to a new location. I am bad at Javascript and have spent hours trying to do this so I think it's time I get some help..
What I am trying to do is get the user to plot two points(markers) draggable on the map. I then want to be able to have my script write the positions(lat,long) to the document. I will then calculate the distance between these as part of a shipping app I am making.
I would like to have the contents of the document (lat,long) updated when a marker(point) is dragged to a new location.
Also, I fixed a schoolboy error where the point vars were being decalred inside the switch statement. My problem is fixed by moving the Add event listener statements inside the switch statement. Thanks Cannonade :)
The next thing now is to try and calculate the distance (crow flies) between the two points
Again, thanks for you help.. appreciated as always!!
Updated Code that works:
var map = null;
var geocoder = null;
var zoom = 15;
var first_point = false;
var boundary = new Array();
var cCount = 0;
var point1;
var point2;
function initialize() {
if (GBrowserIsCompatible()) {
first_point = false;
map = new GMap2(document.getElementById("map_canvas"));
var center = new GLatLng(37.4419, -122.1419);
map.setCenter(center, zoom);
GEvent.addListener(map, "click", function(overlay,point)
{
if (overlay != null)
{}
else
{
var n = boundary.length;
switch (cCount)
{
case 0:
point1 = new GMarker(point,{draggable: true});
map.addOverlay(point1);
cCount++;
GEvent.addListener(point1, "dragend", function()
{
alert('P1 Dragged');
});
break;
case 1:
point2 = new GMarker(point,{draggable: true});
map.addOverlay(point2);
cCount++;
GEvent.addListener(point2, "dragend", function()
{
alert('P2 Dragged');
});
break;
case 2:
map.clearOverlays();
cCount=0;
break;
}
}
});
map.addControl(new GSmallMapControl());
geocoder = new GClientGeocoder();
}
}
I have taken your code and made the following fixes:
Fixed the unbalanced brackets I mentioned in the comment.
Moved the two addListener calls into the switch statement so that the point1 and point2 variables are still in scope when you attach the events.
You can check out the example here (source).
Edit:
Here is some Javascript code to get the linear distance between two points (in meters):
/* Convert degress to radians */
function deg2rad(deg) {
return deg / (180 / Math.PI);
}
/* Calculate distance between two points */
function point_distance(a, b) {
var r = 6378700;
var lat1 = a.y;
var lat2 = b.y;
var lon1 = a.x;
var lon2 = b.x;
var dist = r * Math.acos(Math.sin(deg2rad(lat1)) * Math.sin(deg2rad(lat2)) +
Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) *
Math.cos(deg2rad(lon1 - lon2)));
return dist;
}
This is based on the approximate radius of the earth being 6378700m.