How can I enable animation of TripsLayer without react? TripsLayer example uses React and I really don't know, how to convert it to pure js. Please look at "animate" function in the code below. I tried to update state of layer but it doesn't work (there is no animation of TripsLayer). I don't know, where should I assign "time" variable.
Demo of TripsLayer: https://deck.gl/#/examples/custom-layers/trip-routes
React version my code: https://github.com/uber/deck.gl/blob/master/examples/website/trips/app.js
Docs for TripsLayer: https://github.com/uber/deck.gl/tree/master/modules/experimental-layers/src/trips-layer
My code:
import {MapboxLayer} from '#deck.gl/mapbox';
import {TripsLayer} from '#deck.gl/experimental-layers';
import mapboxgl from 'mapbox-gl';
class App
{
constructor()
{
this.stateInfo = { time: 600 };
}
get state()
{
return this.stateInfo;
}
animate() {
var loopLength = 18000; // unit corresponds to the timestamp in source data
var animationSpeed = 30; // unit time per second
const timestamp = Date.now() / 1000;
const loopTime = loopLength / animationSpeed;
var time = Math.round(((timestamp % loopTime) / loopTime) * loopLength);
// HOW SHOULD I USE time VARIABLE???
window.requestAnimationFrame(this.animate.bind(this));
}
}
var obido = new App();
var tripsLayerObido = new TripsLayer({
id: 'trips',
data: 'trips/tripsKR.json',
getPath: d => d.segments,
getColor: d => (d.vendor === 0 ? [253, 128, 93] : [23, 184, 190]),
opacity: 0.6,
strokeWidth: 30,
trailLength: 180,
currentTime: obido.state.time
});
const LIGHT_SETTINGS = {
lightsPosition: [-74.05, 40.7, 8000, -73.5, 41, 5000],
ambientRatio: 0.05,
diffuseRatio: 0.6,
specularRatio: 0.8,
lightsStrength: [2.0, 0.0, 0.0, 0.0],
numberOfLights: 2
};
export const INITIAL_VIEW_STATE = {
longitude: 19.93,
latitude: 50.03,
zoom: 12.8,
maxZoom: 19,
pitch: 60,
bearing: 0
};
mapboxgl.accessToken = "XXX";
const map = new mapboxgl.Map({
container: 'app',
style: 'mapbox://styles/elninopl/cjnlge6rl094w2so70l1cf8y5',
center: [INITIAL_VIEW_STATE.longitude, INITIAL_VIEW_STATE.latitude],
zoom: INITIAL_VIEW_STATE.zoom,
pitch: INITIAL_VIEW_STATE.pitch,
layers: [tripsLayerObido]
});
map.on('load', () => {
obido.animate(0);
});
Please try setProps, which makes my trips layer updates:
this.tripsLayer.setProps({ currentTime: this.currentTime });
yes,it works.
function animate(timestamp) {
var timestamp = Date.now() / 1000;
var loopTime = loopLength / animationSpeed;
curtime= ((timestamp % loopTime) / loopTime) * loopLength;
requestAnimationFrame(animate);
tripLayer.setProps({ currentTime: curtime });
}
I'm a bit late here, but here there is an example of using deck.gl TripsLayer with the Scripting API (without React).
<script src="https://unpkg.com/deck.gl#^8.4.0/dist.min.js"></script>
<script src="https://unpkg.com/#deck.gl/carto#^8.4.0/dist.min.js"></script>
<script src="https://libs.cartocdn.com/mapbox-gl/v1.13.0/mapbox-gl.js"></script>
<link href="https://libs.cartocdn.com/mapbox-gl/v1.13.0/mapbox-gl.css" rel="stylesheet" />
<div id="map" style="width: 100vw; height: 100vh"></div>
const ambientLight = new deck.AmbientLight({
color: [255, 255, 255],
intensity: 1.0
});
const pointLight = new deck.PointLight({
color: [255, 255, 255],
intensity: 2.0,
position: [-74.05, 40.7, 8000]
});
const lightingEffect = new deck.LightingEffect({ ambientLight, pointLight });
const material = {
ambient: 0.1,
diffuse: 0.6,
shininess: 32,
specularColor: [60, 64, 70]
};
const theme = {
buildingColor: [74, 80, 87],
trailColor0: [253, 128, 93],
trailColor1: [23, 184, 190],
material,
effects: [lightingEffect]
};
const LOOP_LENGTH = 1800;
const ANIMATION_SPEED = 0.4;
async function initialize() {
deck.carto.setDefaultCredentials({
username: 'public',
apiKey: 'default_public',
});
// Fetch Data from CARTO
// Notice that you can use any Deck.gl layer with CARTO datasets getting GeoJSON data from CARTO's API. This method is recommended for complex layers with datasets below 50Mb
const tripsUrl = 'https://public.carto.com/api/v2/sql?q=SELECT the_geom, vendor, timestamps FROM sf_trips_v7&format=geojson';
const geojsonTripsData = await fetch(tripsUrl).then(response => response.json());
// TripsLayer needs data in the following format
const layerData = geojsonTripsData.features.map(f => ({
vendor: f.properties.vendor,
timestamps: f.properties.timestamps,
path: f.geometry.coordinates[0]
}));
const staticLayers = [
// This is only needed when using shadow effects
new deck.PolygonLayer({
id: 'ground-layer',
data: [[[-74.0, 40.7], [-74.02, 40.7], [-74.02, 40.72], [-74.0, 40.72]]],
getPolygon: f => f,
stroked: false,
getFillColor: [0, 0, 0, 0]
}),
new deck.carto.CartoSQLLayer({
id: 'buildings-layer',
data: 'SELECT the_geom_webmercator, height FROM sf_buildings',
extruded: true,
wireframe: true,
opacity: 0.5,
getElevation: f => f.properties.height,
getFillColor: [74, 80, 87],
material: theme.material
})
];
// Create Deck.GL map
const deckgl = new deck.DeckGL({
container: 'map',
mapStyle: 'https://basemaps.cartocdn.com/gl/dark-matter-nolabels-gl-style/style.json',
initialViewState: {
longitude: -74,
latitude: 40.73,
zoom: 12,
pitch: 45,
bearing: 0
},
controller: true,
layers: staticLayers,
effects: theme.effects
});
let time = 0;
function animate() {
time = (time + ANIMATION_SPEED) % LOOP_LENGTH;
window.requestAnimationFrame(animate);
}
setInterval(() => {
deckgl.setProps({
layers: [
...staticLayers,
new deck.TripsLayer({
id: 'trips-layer',
data: layerData,
getPath: d => d.path,
getTimestamps: d => d.timestamps,
getColor: d => (d.vendor === 0 ? theme.trailColor0 : theme.trailColor1),
opacity: 0.3,
widthMinPixels: 2,
rounded: true,
trailLength: 180,
currentTime: time,
shadowEnabled: false
})
]
});
}, 50);
window.requestAnimationFrame(animate);
}
initialize();
Related
So I've been trying to use the RexRainbow Phaser UI plugin, and All the Ui i make is invisible for some reason, But when I draw boundaries, it draws them, leaving me with a bunch of red boxes. Why are they all invisible?
Code Here (Github Gist)
//UI
var tabs = this.rexUI.add
.tabs({
x: 400,
y: 1600,
panel: this.rexUI.add.gridTable({
background: this.rexUI.add.roundRectangle(
0,
0,
20,
10,
10,
0x4e342e
),
table: {
width: 250,
height: 400,
cellWidth: 120,
cellHeight: 60,
columns: 1,
mask: {
padding: 2,
},
},
slider: {
//scroll bar
track: this.rexUI.add.roundRectangle(
0,
0,
20,
10,
10,
this.COLOR_DARK
),
thumb: this.rexUI.add.roundRectangle(
0,
0,
5,
40,
10,
this.COLOR_LIGHT
),
}
.layout()
.drawBounds(this.add.graphics(), 0xff0000); //debug for ui
https://codepen.io/vatsadev/pen/dyqGNBG -> full working example
It is hard too say, but I just can assume, that the reason is, that the color's used (that are not visible) are probally undefined and that's why transparent/invisible.
Without knowing the whole code, it is best to check, the variables/properties used for colors (like: this.COLOR_LIGHT, this.COLOR_DARK, ...)
Especially line 62, since here the this context is local to the tabs- object, and is not the scene object.
Tipp: for debugging purposes, I would hardcode all colors, just to see if the setup works, as intended. If so start replacing the hardcoded values with variables, like this you will find the culprit fast.
document.body.style = 'margin:0;';
const COLOR_PRIMARY = 0x4e342e;
const COLOR_LIGHT = 0x7b5e57;
const COLOR_DARK = 0x260e04;
var config = {
type: Phaser.AUTO,
width: 536,
height: 283,
scene: {
preload,
create
}
};
var isLeaking = false;
function preload (){
this.load.image('tiles', 'https://labs.phaser.io/assets/tilemaps/tiles/catastrophi_tiles_16.png');
this.load.tilemapCSV('map', 'https://labs.phaser.io/assets/tilemaps/csv/catastrophi_level2.csv');
this.load.scenePlugin({
key: "rexuiplugin",
url: "https://raw.githubusercontent.com/rexrainbow/phaser3-rex-notes/master/dist/rexuiplugin.min.js",
sceneKey: "rexUI",
});
}
function create () {
let map = this.make.tilemap({ key: 'map', tileWidth: 16, tileHeight: 16 });
let tileset = map.addTilesetImage('tiles');
let fgLayer = map.createLayer(0, tileset, 0, 0);
createUi(this);
updateMap(map);
}
function updateMap (map) {
let originPoint1 = map.getTileAtWorldXY(200, 100);
console.info(map.layers.sort((a,b) => b.depth - a.depth))
map.forEachTile(function (tile) {
var dist = Phaser.Math.Distance.Chebyshev(
originPoint1.x,
originPoint1.y,
tile.x,
tile.y
);
tile.setAlpha(1 - 0.09 * dist);
});
}
function createDataBase () {
var inventory = ['grass', 2, 'dirt', 3, 'wood', 2, 'leaves', 2, ]
// Create the database
var db = new loki();
// Create a collection
var items = db.addCollection("items");
// Insert documents
for (var i = 0; i < inventory.length; i+=2) {
items.insert({
blockType: inventory[i],
quantity: inventory[i+1],
color: Phaser.Math.Between(0, 0xffffff),
});
}
return items;
};
function createUi(scene){
var db = createDataBase();
var tabs = scene.rexUI.add
.tabs({
x: 250,
y: 250,
panel: scene.rexUI.add.gridTable({
background: scene.rexUI.add.roundRectangle(
0,
0,
20,
10,
10,
COLOR_PRIMARY
),
table: {
width: 250,
height: 400,
cellWidth: 120,
cellHeight: 60,
columns: 1,
mask: {
padding: 2,
},
},
slider: { //scroll bar
track: scene.rexUI.add.roundRectangle(0, 0, 20, 10, 10, COLOR_DARK),
thumb: scene.rexUI.add.roundRectangle(0, 0, 5, 40, 10, COLOR_LIGHT),
},
createCellContainerCallback: function (cell) { // each inventory cell
var scene = cell.scene;
var width = 250;
var height = cell.height;
var item = cell.item;
var index = cell.index;
return scene.rexUI.add.label({
width: width,
height: height,
background: scene.rexUI.add
.roundRectangle(0, 0, 20, 20, 0)
.setStrokeStyle(2, COLOR_DARK),
icon: scene.rexUI.add.roundRectangle( // inventory item texture goes here
0,
0,
20,
20,
10,
item.color
),
text: scene.add.text(0, 0, `${item.blockType}: ${item.quantity}`),
space: {
icon: 10,
left: 15,
},
});
},
}),
leftButtons: [
createButton(scene, 2, "Inv."),
],
space: {
leftButtonsOffset: 20,
leftButton: 1,
},
})
.layout()
.drawBounds(scene.add.graphics(), 0xff0000);
tabs.on(
"button.click",
function () {
// Load items into grid table
var items = db
.chain()
.data();
this.getElement("panel").setItems(items).scrollToTop();
},
tabs
);
tabs.emitButtonClick("left", 0);
}
function createButton (scene, direction, text) {
var radius;
switch (direction) {
case 0: // Right
radius = {
tr: 20,
br: 20,
};
break;
case 2: // Left
radius = {
tl: 20,
bl: 20,
};
break;
}
return scene.rexUI.add.label({
width: 50,
height: 40,
background: scene.rexUI.add.roundRectangle(
0,
0,
50,
50,
radius,
COLOR_DARK
),
text: scene.add.text(0, 0, text, {
fontSize: "18pt",
}),
space: {
left: 10,
},
});
};
new Phaser.Game(config);
<script src="//cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lokijs/1.5.5/lokijs.min.js"></script>
I am trying to make a shooting game in matter.js but can't find a way to shoot bullets from the player's exact location and how to count the collision between player and bullet but not with the walls.
I want to fire a bullet from player1 and then on pressing D again it should fire another bullet from the player1's last position.
My Codepen of this game
let p1= Matter.Bodies.polygon(200, 200, 3, 40, {
chamfer: {
radius: [15,10,15]
},
isStatic: false,
inertia: Infinity,
friction: 0.9,
render: {
fillStyle: '#F9ED69'
},
mass:1
});
let p2 = Matter.Bodies.polygon(1100, 200, 3, 40, {
chamfer: {
radius: [15,10,15]
},
isStatic: false,
inertia: Infinity,
friction: 0.9,
render: {
fillStyle: '#11999E'
},
mass:1
});
let bullet1 = Matter.Bodies.polygon(400, 300, 3, 7, {
chamfer: {
radius: [4,2,4]
},
isStatic: false,
inertia: Infinity,
friction: 0.9,
render: {
fillStyle: '#F9ED69'
},
mass:0
});
const keyHandlers = {
KeyS: () => {
Matter.Body.applyForce(p1, {
x: p1.position.x,
y: p1.position.y
}, {x: 0.0, y: 0.001})
},
KeyW: () => {
Matter.Body.applyForce(p1, {
x: p1.position.x,
y: p1.position.y
}, {x: 0.0, y: -0.002})
},
KeyD:()=>{
Matter.Body.applyForce(bullet1, {
x: p1.position.x,
y: p1.position.y
}, {x: 0.001, y: 0.0})
},
};
const keysDown = new Set();
document.addEventListener("keydown", event => {
keysDown.add(event.code);
});
document.addEventListener("keyup", event => {
keysDown.delete(event.code);
});
Matter.Events.on(engine, "beforeUpdate", event => {
[...keysDown].forEach(k => {
keyHandlers[k]?.();
});
});
// on collision of a bullet with wall and other bodies remove the bullet from the world after some delay and add the score
let score1 = 0;
let score2 = 0;
let health
Matter.Events.on(engine, "collisionStart", event => {
for (let i = 0; i < event.pairs.length; i++) {
const pair = event.pairs[i];
if (pair.bodyA === bullet1 || pair.bodyB === bullet1) {
Matter.World.remove(engine.world, bullet1);
alert('1');
}
if (pair.bodyA === bullet2 || pair.bodyB === bullet2) {
Matter.World.remove(engine.world, bullet2);
alert('2');
}
}
score1++;
alert(`SCore1 is ${score1}`); // these alerts are just to confirm the collision
});
You're on the right track, but if you hardcode bullet1 and bullet2 you're stuck with just those two bullets. Even with a fixed number of bullets and re-using the bodies (good for performance but maybe premature optimization), I'd probably use an array to store these bullets, which is almost always the correct move after you catch yourself doing thing1, thing2...
Here's a proof of concept. I'm creating and destroying bullets here to keep the coding easier, but it'd be more performant to keep a pool of objects and re-use/re-position them.
I'm also using sets to keep track of the types of the bodies, but you might want to use labels. Most of the code here could go in many different directions, specific to your use case.
const engine = Matter.Engine.create();
engine.gravity.y = 0; // enable top-down
const map = {width: 300, height: 300};
const render = Matter.Render.create({
element: document.body,
engine,
options: {...map, wireframes: false},
});
const player = {
score: 0,
body: Matter.Bodies.polygon(
map.width / 2, map.height / 2, 3, 15, {
frictionAir: 0.06,
density: 0.9,
render: {fillStyle: "red"},
},
),
lastShot: Date.now(),
cooldown: 150,
fireForce: 0.1,
rotationAngVel: 0.03,
rotationAmt: 0.03,
rotateLeft() {
Matter.Body.rotate(this.body, -this.rotationAmt);
Matter.Body.setAngularVelocity(
this.body, -this.rotationAngVel
);
},
rotateRight() {
Matter.Body.rotate(this.body, this.rotationAmt);
Matter.Body.setAngularVelocity(
this.body, this.rotationAngVel
);
},
fire() {
if (Date.now() - this.lastShot < this.cooldown) {
return;
}
// move the bullet away from the player a bit
const {x: bx, y: by} = this.body.position;
const x = bx + (Math.cos(this.body.angle) * 10);
const y = by + (Math.sin(this.body.angle) * 10);
const bullet = Matter.Bodies.circle(
x, y, 4, {
frictionAir: 0.006,
density: 0.1,
render: {fillStyle: "yellow"},
},
);
bullets.add(bullet);
Matter.Composite.add(engine.world, bullet);
Matter.Body.applyForce(
bullet, this.body.position, {
x: Math.cos(this.body.angle) * this.fireForce,
y: Math.sin(this.body.angle) * this.fireForce,
},
);
this.lastShot = Date.now();
},
};
const bullets = new Set();
const makeEnemy = () => Matter.Bodies.polygon(
(Math.random() * (map.width - 40)) + 20,
(Math.random() * (map.height - 40)) + 20,
5, 6, {
render: {
fillStyle: "transparent",
strokeStyle: "white",
lineWidth: 1,
},
},
);
const enemies = new Set([...Array(100)].map(makeEnemy));
const walls = new Set([
Matter.Bodies.rectangle(
0, map.height / 2, 20, map.height, {isStatic: true}
),
Matter.Bodies.rectangle(
map.width / 2, 0, map.width, 20, {isStatic: true}
),
Matter.Bodies.rectangle(
map.width, map.height / 2, 20, map.height, {isStatic: true}
),
Matter.Bodies.rectangle(
map.width / 2, map.height, map.width, 20, {isStatic: true}
),
]);
Matter.Composite.add(engine.world, [
player.body, ...walls, ...enemies
]);
const keyHandlers = {
ArrowLeft: () => player.rotateLeft(),
ArrowRight: () => player.rotateRight(),
Space: () => player.fire(),
};
const validKeys = new Set(Object.keys(keyHandlers));
const keysDown = new Set();
document.addEventListener("keydown", e => {
if (validKeys.has(e.code)) {
e.preventDefault();
keysDown.add(e.code);
}
});
document.addEventListener("keyup", e => {
if (validKeys.has(e.code)) {
e.preventDefault();
keysDown.delete(e.code);
}
});
Matter.Events.on(engine, "beforeUpdate", event => {
[...keysDown].forEach(k => {
keyHandlers[k]?.();
});
if (enemies.size < 100 && Math.random() > 0.95) {
const enemy = makeEnemy();
enemies.add(enemy);
Matter.Composite.add(engine.world, enemy);
}
});
Matter.Events.on(engine, "collisionStart", event => {
for (const {bodyA, bodyB} of event.pairs) {
const [a, b] = [bodyA, bodyB].sort((a, b) =>
bullets.has(a) ? -1 : 1
);
if (bullets.has(a) && walls.has(b)) {
Matter.Composite.remove(engine.world, a);
bullets.delete(a);
}
else if (bullets.has(a) && enemies.has(b)) {
Matter.Composite.remove(engine.world, a);
Matter.Composite.remove(engine.world, b);
bullets.delete(a);
enemies.delete(b);
document.querySelector("span").textContent = ++player.score;
}
}
});
Matter.Render.run(render);
Matter.Runner.run(Matter.Runner.create(), engine);
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.18.0/matter.min.js"></script>
<div>press left/right arrow keys to rotate and space to shoot</div>
<div>score: <span>0</span></div>
Im using the hexagon layer example from deck.gl as my base and trying to find a way to include a control panel similar to their online website https://deck.gl/examples/hexagon-layer/
import React from 'react';
import {render} from 'react-dom';
import {StaticMap} from 'react-map-gl';
import {AmbientLight, PointLight, LightingEffect} from '#deck.gl/core';
import {HexagonLayer} from '#deck.gl/aggregation-layers';
import DeckGL from '#deck.gl/react';
import {Controller} from 'deck.gl';
// Source data CSV
const DATA_URL =
'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/3d-heatmap/heatmap-data.csv'; // eslint-disable-line
const ambientLight = new AmbientLight({
color: [255, 255, 255],
intensity: 1.0
});
const pointLight1 = new PointLight({
color: [255, 255, 255],
intensity: 0.8,
position: [-0.144528, 49.739968, 80000]
});
const pointLight2 = new PointLight({
color: [255, 255, 255],
intensity: 0.8,
position: [-3.807751, 54.104682, 8000]
});
const lightingEffect = new LightingEffect({ambientLight, pointLight1, pointLight2});
const OPTIONS = ['radius', 'coverage', 'upperPercentile'];
const material = {
ambient: 0.64,
diffuse: 0.6,
shininess: 32,
specularColor: [51, 51, 51]
};
const INITIAL_VIEW_STATE = {
longitude: -1.415727,
latitude: 52.232395,
zoom: 6.6,
minZoom: 5,
maxZoom: 15,
pitch: 40.5,
bearing: -27
};
const MAP_STYLE = 'https://basemaps.cartocdn.com/gl/dark-matter-nolabels-gl-style/style.json';
export const colorRange = [
[1, 152, 189],
[73, 227, 206],
[216, 254, 181],
[254, 237, 177],
[254, 173, 84],
[209, 55, 78]
];
OPTIONS.forEach(key => {
document.getElementById(key).oninput = renderLayer;
});
renderLayer();
function renderLayer () {
const options = {};
OPTIONS.forEach(key => {
const value = +document.getElementById(key).value;
document.getElementById(key + '-value').innerHTML = value;
options[key] = value;
});
function getTooltip({object}) {
if (!object) {
return null;
}
const lat = object.position[1];
const lng = object.position[0];
const count = object.points.length;
return `\
latitude: ${Number.isFinite(lat) ? lat.toFixed(6) : ''}
longitude: ${Number.isFinite(lng) ? lng.toFixed(6) : ''}
${count} Accidents`;
}
/* eslint-disable react/no-deprecated */
export default function App({
data,
mapStyle = MAP_STYLE,
radius = 1000,
upperPercentile = 100,
coverage = 1
}) {
const layers = [
new HexagonLayer({
id: 'heatmap',
colorRange,
data,
elevationRange: [0, 3000],
elevationScale: data && data.length ? 50 : 0,
extruded: true,
getPosition: d => d,
pickable: true,
material,
coverage,
radius,
upperPercentile,
transitions: {
elevationScale: 3000
}
})
];
return (
<DeckGL
layers={layers}
effects={[lightingEffect]}
initialViewState={INITIAL_VIEW_STATE}
controller={true}
getTooltip={getTooltip}
>
<StaticMap reuseMaps mapStyle={mapStyle} preventStyleDiffing={true}/>
</DeckGL>
);
}
export function renderToDOM(container) {
render(<App />, container);
require('d3-request').csv(DATA_URL, (error, response) => {
if (!error) {
const data = response.map(d => [Number(d.lng), Number(d.lat)]);
render(<App data={data} />, container);
}
});
}
This is what the happens if i run the example code without the control panel
This is the what loads when i run the current code (example with my additions
I am new to javascript,
so any help is much appreciated!
I think you forgot to add mapboxApiAccessToken to your StaticMap component because react-map-gl is a Mapbox map library for React js.
const MAPBOX_TOKEN = '...'
// some code here
return(
// some code here
<StaticMap
reuseMaps
mapStyle={mapStyle}
preventStyleDiffing={true}
mapboxApiAccessToken={MAPBOX_TOKEN}
/>
// some code here
)
You can get the value of Mapbox token from here https://account.mapbox.com/access-tokens/
I'm using Ammo.js, a direct JavaScript port of C++ Bullet Physics. The unfortunate result being that the documentation is C++, not great reading if your languages are Python and JavaScript.
I have the documentation for Ammo.btCompoundShape here but can't make sense of it.
I have a working code here where the Bone instance just falls through the floor, as you'll see. Don't worry about the naming of "Bone", at this stage in development it's just meant to test a compound shape of two blocks.
class RenderEngine {
constructor(gameEngine) {
this.gameEngine = gameEngine
this.gameEngine.clock = new THREE.Clock();
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(0xbfd1e5);
this.camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.2, 5000);
this.camera.position.set(0, 30, 70);
this.camera.lookAt(new THREE.Vector3(0, 0, 0));
const hemiLight = new THREE.HemisphereLight(0xffffff, 0xffffff, 0.1);
hemiLight.color.setHSL(0.6, 0.6, 0.6);
hemiLight.groundColor.setHSL(0.1, 1, 0.4);
hemiLight.position.set(0, 50, 0);
this.scene.add(hemiLight);
const dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.color.setHSL(0.1, 1, 0.95);
dirLight.position.set(-1, 1.75, 1);
dirLight.position.multiplyScalar(100);
this.scene.add(dirLight);
dirLight.castShadow = true;
dirLight.shadow.mapSize.width = 2048;
dirLight.shadow.mapSize.height = 2048;
const d = 50;
dirLight.shadow.camera.left = -d;
dirLight.shadow.camera.right = d;
dirLight.shadow.camera.top = d;
dirLight.shadow.camera.bottom = -d;
dirLight.shadow.camera.far = 13500;
this.renderer = new THREE.WebGLRenderer({
antialias: true
});
this.renderer.setClearColor(0xbfd1e5);
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(this.renderer.domElement);
this.renderer.shadowMap.enabled = true;
}
renderFrame() {
this.renderer.render(this.scene, this.camera)
}
}
class PhysicsEngine {
constructor(gameEngine, physicsEngine) {
this.gameEngine = gameEngine
let collisionConfiguration = new Ammo.btDefaultCollisionConfiguration(),
dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration),
overlappingPairCache = new Ammo.btDbvtBroadphase(),
solver = new Ammo.btSequentialImpulseConstraintSolver();
this.tmpTrans = new Ammo.btTransform();
this.physicsWorld = new Ammo.btDiscreteDynamicsWorld(dispatcher, overlappingPairCache, solver, collisionConfiguration);
this.physicsWorld.setGravity(new Ammo.btVector3(0, -10, 0));
}
updateFrame() {
this.physicsWorld.stepSimulation(this.gameEngine.clock.getDelta(), 10);
this.gameEngine.objects.forEach(object => {
const ms = object.ammo.getMotionState()
if (ms) {
ms.getWorldTransform(this.tmpTrans)
const p = this.tmpTrans.getOrigin()
const q = this.tmpTrans.getRotation()
object.mesh.position.set(p.x(), p.y(), p.z())
object.mesh.quaternion.set(q.x(), q.y(), q.z(), q.w())
}
})
}
}
class GameEngine {
constructor(renderEngine, physicsEngine) {
this.objects = []
this.renderEngine = new RenderEngine(this, renderEngine)
this.physicsEngine = new PhysicsEngine(this, physicsEngine)
}
run() {
this.physicsEngine.updateFrame()
this.renderEngine.renderFrame()
requestAnimationFrame(() => {
this.run()
});
}
add(object) {
this.objects.push(object)
return this.objects.length - 1
}
remove(objectIndex) {
this.objects[objectIndex] = false
}
}
class Box {
constructor(gameEngine, properties) {
this.gameEngine = gameEngine
this._initPhysics_(properties)
this._initRendering_(properties)
this.id = gameEngine.add(this)
}
_initPhysics_(properties) {
const pos = properties.pos
const quat = properties.quat
const scale = properties.scale
const mass = properties.mass
const group = properties.group
const interactionGroup = properties.interactionGroup
const physicsWorld = this.gameEngine.physicsEngine.physicsWorld
const transform = new Ammo.btTransform()
transform.setIdentity()
transform.setOrigin(new Ammo.btVector3(pos.x, pos.y, pos.z))
transform.setRotation(new Ammo.btQuaternion(quat.x, quat.y, quat.z, quat.w))
const motionState = new Ammo.btDefaultMotionState(transform)
const colShape = new Ammo.btBoxShape(new Ammo.btVector3(scale.x * 0.5, scale.y * 0.5, scale.z * 0.5))
colShape.setMargin(0.05)
const localInertia = new Ammo.btVector3(0, 0, 0)
colShape.calculateLocalInertia(mass, localInertia)
const rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, colShape, localInertia)
const body = new Ammo.btRigidBody(rbInfo)
physicsWorld.addRigidBody(body, group, interactionGroup)
this.ammo = body
}
_initRendering_(properties) {
const pos = properties.pos
const scale = properties.scale
const color = properties.color
this.mesh = new THREE.Mesh(new THREE.BoxBufferGeometry(), new THREE.MeshPhongMaterial({
color
}))
this.mesh.position.set(pos.x, pos.y, pos.z)
this.mesh.scale.set(scale.x, scale.y, scale.z)
this.mesh.castShadow = true
this.mesh.receiveShadow = true
this.gameEngine.renderEngine.scene.add(this.mesh)
}
}
class Bone {
constructor(gameEngine, properties) {
this.gameEngine = gameEngine
this._initPhysics_(properties)
this._initRendering_(properties)
this.id = gameEngine.add(this)
}
_initPhysics_(properties) {
const pos = properties.pos
const quat = properties.quat
const scale = properties.scale
const mass = properties.mass
const group = properties.group
const interactionGroup = properties.interactionGroup
const physicsWorld = this.gameEngine.physicsEngine.physicsWorld
const compoundShape = new Ammo.btCompoundShape()
this._addSection_(compoundShape, {
pos,
quat,
scale,
offset: {
x: 0,
y: 0,
z: 0
},
rotation: {
x: 0,
y: 0,
z: 0,
w: 0
}
})
this._addSection_(compoundShape, {
pos,
quat,
scale,
offset: {
x: 0,
y: 0,
z: 0
},
rotation: {
x: 0,
y: 0,
z: 0,
w: 0
}
})
const transform = new Ammo.btTransform()
transform.setIdentity()
transform.setOrigin(new Ammo.btVector3(pos.x, pos.y, pos.z))
transform.setRotation(new Ammo.btQuaternion(quat.x, quat.y, quat.z, quat.w))
const motionState = new Ammo.btDefaultMotionState(transform)
compoundShape.setMargin(0.05)
const localInertia = new Ammo.btVector3(0, 0, 0)
compoundShape.calculateLocalInertia(mass, localInertia)
const rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, compoundShape, localInertia)
const body = new Ammo.btRigidBody(rbInfo)
physicsWorld.addRigidBody(body, group, interactionGroup)
this.ammo = body
}
_initRendering_(properties) {
const pos = properties.pos
const scale = properties.scale
const color = properties.color
this.mesh = new THREE.Mesh(new THREE.BoxBufferGeometry(), new THREE.MeshPhongMaterial({
color
}))
this.mesh.position.set(pos.x, pos.y, pos.z)
this.mesh.scale.set(scale.x, scale.y, scale.z)
this.mesh.castShadow = true
this.mesh.receiveShadow = true
this.gameEngine.renderEngine.scene.add(this.mesh)
}
_addSection_(compoundShape, properties) {
const pos = properties.pos
const quat = properties.quat
const scale = properties.scale
const offset = properties.offset
const rotation = properties.rotation
const transform = new Ammo.btTransform()
transform.setIdentity()
transform.setOrigin(new Ammo.btVector3(pos.x + offset.x, pos.y + offset.y, pos.z + offset.z))
transform.setRotation(new Ammo.btQuaternion(quat.x + rotation.x, quat.y + rotation.y, quat.z + rotation.z, quat.w + rotation.w))
const motionState = new Ammo.btDefaultMotionState(transform)
const colShape = new Ammo.btBoxShape(new Ammo.btVector3(scale.x * 0.5, scale.y * 0.5, scale.z * 0.5))
compoundShape.addChildShape(transform, colShape)
}
}
Ammo().then((Ammo) => {
const gameEngine = new GameEngine(THREE, Ammo)
const plane = new Box(gameEngine, {
pos: {
x: 0,
y: 0,
z: 0
},
quat: {
x: 0,
y: 0,
z: 0,
w: 1
},
scale: {
x: 50,
y: 2,
z: 50
},
mass: 0,
group: 1,
interactionGroup: 1,
color: 0xa0afa4
})
const box1 = new Box(gameEngine, {
pos: {
x: 0,
y: 5,
z: 0
},
quat: {
x: 0,
y: 0,
z: 0,
w: 1
},
scale: {
x: 2,
y: 2,
z: 2
},
mass: 1,
group: 1,
interactionGroup: 1,
color: 0xa0afa4
})
const box2 = new Box(gameEngine, {
pos: {
x: 0.75,
y: 8,
z: 0.75
},
quat: {
x: 0,
y: 0,
z: 0,
w: 1
},
scale: {
x: 2,
y: 2,
z: 2
},
mass: 1,
group: 1,
interactionGroup: 1,
color: 0xa0afa4
})
const bone1 = new Bone(gameEngine, {
pos: {
x: -0.75,
y: 10,
z: -0.75
},
quat: {
x: 0,
y: 0,
z: 0,
w: 1
},
scale: {
x: 2,
y: 2,
z: 2
},
mass: 1,
group: 1,
interactionGroup: 1,
color: 0xa0afa4
})
console.log("gameEngine", gameEngine)
gameEngine.run()
})
canvas, body, html {
margin: 0px;
padding: 0px;
overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r124/three.min.js"></script>
<script src="https://cdn.babylonjs.com/ammo.js"></script>
The two Box instances land on the floor (plane), bone1 falls through. I assume I did something wrong with the Ammo.btCompoundShape. There are no errors. What's the correct way?
The bone actually does not fall through completely, it stops in the middle of the plane.
Reason: You are transforming two times:
motionState is transformed
compoundShape is transformed again inside of _addSection_
This way the compoundShape does collide and does not fall through, but the visible colShape is offset (from the reference position of compoundShape) to be inside the plane.
You can see that if you try and change this line inside _addSection_:
transform.setOrigin(new Ammo.btVector3(0, 0, 2.0))
Solution:
Do not transform two times. E.g. transform only the motionState, but not the compoundShape.
E.g. remove these two lines:
_addSection_(compoundShape, properties) {
const transform = new Ammo.btTransform()
transform.setIdentity()
// -- disable second transform: --
// transform.setOrigin(new Ammo.btVector3( ... ))
// transform.setRotation(new Ammo.btQuaternion( ... ))
const colShape = new Ammo.btBoxShape(new Ammo.btVector3(scale.x * 0.5, scale.y * 0.5, scale.z * 0.5))
compoundShape.addChildShape(transform, colShape)
}
Another remark:
localInertia is also applied to two times:
compoundShape.calculateLocalInertia(mass, localInertia) and
const rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, compoundShape, localInertia).
This obviously works, but is probably not intentional. It doesn't fail because it is 0,0,0. If you indeed want two inertia, I also think you can not use the same localInertia object for both of them, but you should create a second object e.g. localInertiaCompoundShape, but I'm not sure.
I am working on an open layers map with different vector layers. I'm trying to show the information of the features in the geojson on the layer via a popup window. I found these two references:
http://jsfiddle.net/zt2tyzqo/2/
https://embed.plnkr.co/plunk/GvdVNE
Thank you for any support. :)
This is the geojson:
{
"type": "FeatureCollection",
"name": "dachnotnull",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{ "type": "Feature", "properties": { "OBJECTID": 9, "GEBFLAECHE": 214.0, "GREENAR05": 13.0, "GREENAR20": 0.0, "SHAPE_AREA": 214.42924681599999, "SHAPE_LEN": 0.0, "p_intensiv": 0.060626058212800003, "p_extensiv": 0.0, "p_gesamt": 0.060626058212800003, "flaeche": 214.42924681599999 }, "geometry": { "type": "MultiPolygon", "coordinates":
This is the main.js:
import 'ol/ol.css';
import Map from 'ol/Map';
import View from 'ol/View';
import TileLayer from 'ol/layer/Tile';
import Stamen from 'ol/source/Stamen';
import VectorLayer from 'ol/layer/Vector';
import Vector from 'ol/source/Vector';
import GeoJSON from 'ol/format/GeoJSON';
import Style from 'ol/style/Style';
import Circle from 'ol/style/Circle';
import Fill from 'ol/style/Fill';
import Stroke from 'ol/style/Stroke';
import Overlay from 'ol/Overlay';
import {
fromLonLat,
toLonLat
} from 'ol/proj';
import sync from 'ol-hashed';
import OSM from 'ol/source/OSM';
import Feature from 'ol/Feature';
import {
circular
} from 'ol/geom/Polygon';
import Point from 'ol/geom/Point';
import Control from 'ol/control/Control';
import * as olProj from 'ol/proj';
import XYZ from 'ol/source/XYZ';
// define the map
const map = new Map({
target: 'map',
view: new View({
center: fromLonLat([16.37, 48.2]),
zoom: 13
})
});
sync(map);
//Adresssuche
const searchResultSource = new Vector();
const searchResultLayer = new VectorLayer({
source: searchResultSource
});
searchResultLayer.setStyle(new Style({
image: new Circle({
fill: new Fill({
color: 'rgba(0, 128, 0, 1)'
}),
stroke: new Stroke({
color: '#000000',
width: 1.25
}),
radius: 15
})
}));
var element = document.getElementById('search');
element.addEventListener('keydown', listenerFunction);
function listenerFunction(event) {
console.log(event);
console.log(event.keyCode);
if (event.keyCode === 13) {
const xhr = new XMLHttpRequest;
xhr.open('GET', 'https://photon.komoot.de/api/?q=' + element.value + '&limit=3');
xhr.onload = function () {
const json = JSON.parse(xhr.responseText);
const geoJsonReader = new GeoJSON({
featureProjection: 'EPSG:3857'
});
searchResultSource.clear();
const features = geoJsonReader.readFeatures(json);
console.log(features);
searchResultSource.addFeatures(features);
if (!searchResultSource.isEmpty()) {
map.getView().fit(searchResultSource.getExtent(), {
maxZoom: 18,
duration: 500
});
}
};
xhr.send();
}
}
//OpenStreetMap
const OSMbaseLayer = new TileLayer({
type: 'base',
source: new OSM()
});
// Statellit
const satellitLayer = new TileLayer({
source: new XYZ({
attributions: ['Powered by Esri', 'Source: Esri, DigitalGlobe, GeoEye, Earthstar Geographics, CNES/Airbus DS, USDA, USGS, AeroGRID, IGN, and the GIS User Community'],
attributionsCollapsible: false,
url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
maxZoom: 30
})
});
//shape
const parkLayer = new VectorLayer({
source: new Vector({
name: 'park',
url: 'data/park1.geojson',
format: new GeoJSON()
})
});
parkLayer.setStyle(new Style({
fill: new Fill({
color: 'green'
}),
stroke: new Stroke({
color: 'green',
width: 1.25
}),
}));
const hitzeLayer = new VectorLayer({
source: new Vector({
name: 'hitze',
url: 'data/hitze.geojson',
format: new GeoJSON()
})
});
hitzeLayer.setStyle(new Style({
fill: new Fill({
color: 'red'
}),
stroke: new Stroke({
color: 'yellow',
width: 1.25
}),
}));
hitzeLayer.setStyle(function(feature) {
let fillColor;
const hitzeindex = feature.get('AVG_UHVI_A');
if (hitzeindex < 0.5) {
fillColor = 'rgba(255, 228, 225, 0.7)';
} else if (hitzeindex < 0.75) {
fillColor = 'rgba(240, 128, 128, 0.7)';
} else {
fillColor = 'rgba(128, 0, 0, 0.7)';
}
return new Style({
fill: new Fill({
color: fillColor
}),
stroke: new Stroke({
color: 'rgba(4, 4, 4, 1)',
width: 1
})
});
});
const dachLayer = new VectorLayer({
minZoom: 17.9999,
source: new Vector({
name: 'dach',
url: 'data/dachnotnull.geojson',
format: new GeoJSON()
})
});
dachLayer.setStyle(function(feature) {
let fillColor;
const begruenung = feature.get('p_gesamt');
if (begruenung < 0.2) {
fillColor = 'rgba(143, 188, 143, 1)';
} else if (begruenung < 0.4) {
fillColor = 'rgba(60, 179, 113, 1)';
} else if (begruenung < 0.6) {
fillColor = 'rgba(46, 139, 87, 1)';
} else if (begruenung < 0.8) {
fillColor = 'rgba(34, 139, 34, 1)';
} else {
fillColor = 'rgba(0, 100, 0, 1)';
}
return new Style({
fill: new Fill({
color: fillColor
})
});
});
// Layer hinzufügen
map.addLayer(OSMbaseLayer);
map.addLayer(searchResultLayer);
dachLayer.setZIndex(15);
parkLayer.setZIndex(10);
hitzeLayer.setZIndex(5);
// gruenflaechen layer anzeigen
const park = document.getElementById('park');
park.addEventListener('click', function (event) {
var checkBox = document.getElementById("park");
if (checkBox.checked == true) {
parkLayer.setMap(map);
// parkLayer.setVisible(true);
} else {
parkLayer.setMap(undefined);
//parkLayer.setVisible(false);
}
});
// hitze layer anzeigen
const hitze = document.getElementById('hitze');
hitze.addEventListener('click', function (event) {
var checkBox = document.getElementById("hitze");
if (checkBox.checked == true) {
hitzeLayer.setMap(map);
//hitzeLayer.setVisible(true);
} else {
//hitzeLayer.setVisible(false);
hitzeLayer.setMap(undefined);
}
});
// dach layer anzeigen
const dach = document.getElementById('dach');
dach.addEventListener('click', function (event) {
var checkBox = document.getElementById("dach");
if (checkBox.checked == true) {
dachLayer.setMap(map);
//hitzeLayer.setVisible(true);
} else {
//hitzeLayer.setVisible(false);
dachLayer.setMap(undefined);
}
});
// Get the OSMbase Base-Button
const OSMbase = document.getElementById('OSMbase');
OSMbase.addEventListener('click', function (event) {
//contr.style.color = 'ffffff';
//Andere Layer entfernen
map.removeLayer(satellitLayer);
map.removeLayer(searchResultLayer);
//OSM Layer hinzufügen
map.addLayer(OSMbaseLayer);
map.addLayer(searchResultLayer);
});
// Get the satellit Base-Button
const satellit = document.getElementById('satellit');
satellit.addEventListener('click', function (event) {
//Andere Layer entfernen
map.removeLayer(OSMbaseLayer);
map.removeLayer(searchResultLayer);
//Satelliten Layer hinzufügen
map.addLayer(satellitLayer);
map.addLayer(searchResultLayer);
});
//GPS Location
const GPSsource = new Vector();
const GPSlayer = new VectorLayer({
source: GPSsource
});
map.addLayer(GPSlayer);
navigator.geolocation.watchPosition(function (pos) {
const coords = [pos.coords.longitude, pos.coords.latitude];
const accuracy = circular(coords, pos.coords.accuracy);
GPSsource.clear(true);
GPSsource.addFeatures([
new Feature(accuracy.transform('EPSG:4326', map.getView().getProjection())),
new Feature(new Point(fromLonLat(coords)))
]);
}, function (error) {
alert(`ERROR: ${error.message}`);
}, {
enableHighAccuracy: true
});
const locate = document.createElement('div');
locate.className = 'ol-control ol-unselectable locate';
locate.innerHTML = '<button title="Locate me">◎</button>';
locate.addEventListener('click', function () {
if (!GPSsource.isEmpty()) {
map.getView().fit(GPSsource.getExtent(), {
maxZoom: 18,
duration: 500
});
}
});
map.addControl(new Control({
element: locate
}));
you need to add a overlay to your map object;
use setPosition of your overlay object to snap to a point
example:
https://openlayers.org/en/latest/examples/popup.html
api: https://openlayers.org/en/latest/apidoc/module-ol_Overlay-Overlay.html