Three.js - rotating a camera around a point - javascript

I have my custom Navigation control similar to Trackball but with additional functionality.
One of the features I want to implement is the specifying center of rotation of the camera and when we pan a camera, the rotation maintained around the specified point.
Here is the code of my update() function:
update(): this {
const camera = this.camera
if (this._enabled && camera && this._isInvalid) {
const cameraObject = camera.object
// validate all navigation changes
{
const {
offset,
panOffset,
axis,
tempVector3,
tempSpherical,
deltaRotation,
deltaPan,
quaternion
} = this.computeHelpers
// offset vector from eye to target
offset.subVectors(this._eye, this._target)
// apply rotation
if (deltaRotation.isInvalid) {
const { x: theta, y: phi } = deltaRotation.get()
// rotate around screen-space y-axis
if (theta) {
axis.set(0, 1, 0).applyQuaternion(cameraObject.quaternion)
quaternion.setFromAxisAngle(axis, theta)
offset.applyQuaternion(quaternion)
this._up.applyQuaternion(quaternion)
}
// rotate around screen-space x-axis
if (phi) {
axis.set(1, 0, 0).applyQuaternion(cameraObject.quaternion)
quaternion.setFromAxisAngle(axis, phi)
offset.applyQuaternion(quaternion)
this._up.applyQuaternion(quaternion)
}
// mark compute objects as valid
deltaRotation.set({ x: 0, y: 0 })
deltaRotation.isInvalid = false
}
// apply panning
if (deltaPan.isInvalid) {
cameraObject.updateMatrix()
const matrix = cameraObject.matrix
const { x: deltaPanX, y: deltaPanY } = deltaPan.get()
// pan screen space x-direction
{
tempVector3
// get x-column of local transform matrix
.setFromMatrixColumn(matrix, 0)
.multiplyScalar(deltaPanX)
panOffset.add(tempVector3)
}
// pan screen space y-direction
{
tempVector3
// get y-column of local transform matrix
.setFromMatrixColumn(matrix, 1)
.multiplyScalar(-deltaPanY)
panOffset.add(tempVector3)
}
this._target.add(panOffset)
// mark compute objects as valid
panOffset.set(0, 0, 0)
deltaPan.set({ x: 0, y: 0 })
deltaPan.isInvalid = false
}
// assemble new eye (camera) position
this._eye.addVectors(offset, this._target)
this._eyeSpherical.setFromVector3(this._eye)
this._direction.copy(this._eye).normalize()
}
// update camera position
cameraObject.position.copy(this._eye)
cameraObject.up.copy(this._up)
cameraObject.lookAt(this._target)
// mark the controller as valid
this._isInvalid = false
}
return this
}
How can I apply the translation?
Expected result:
Thanks a lot in advance

Related

Transparency of multiple tile layers without colors leaking (phaser.js)

So I have been using the Chebyshev Distance example from the Phaser labs, and while this example was using one layer, I happen to be using two, and when i set transparency on them, the colors start leaking into each other, especially on light colors.
Is there any way to circumvent or get rid of this effect
If the problem is that you have two layers, one ontop of the other and you are making both transparent (or only the top one), and you don't want that color to pass through, the solution could be to hide the tiles on the bottom layer.
Just check in the map-tile-loop, if the tile, where you want to change the alpha, has a tile beneath it, and if so make that background tile transparent.
Here a small working demo:
(The main magic is in the updateMap function)
document.body.style = 'margin:0;';
var config = {
type: Phaser.AUTO,
width: 536,
height: 183,
scene: {
preload,
create
}
};
var player;
var bgLayer;
var point1 = {x: 250, y: 31};
var isLeaking = false;
new Phaser.Game(config);
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');
}
function create () {
this.add.text(50, 1, ' <- Background is visible, if no tiles are ontop')
.setOrigin(0)
.setDepth(100)
.setStyle({fontFamily: 'Arial'});
this.infoText = this.add.text(10, 20, 'Click to toggle leaking: on')
.setOrigin(0)
.setDepth(100)
.setStyle({fontFamily: 'Arial'});
// Just creating image for second layer tiles //
let graphics = this.make.graphics();
graphics.fillStyle(0xff0000);
graphics.fillRect(0, 0, 16, 16);
graphics.generateTexture('tiles2', 16, 16);
// Just creating image for second layer tiles //
let map = this.make.tilemap({ key: 'map', tileWidth: 16, tileHeight: 16 });
let tileset = map.addTilesetImage('tiles');
let tileset2 = map.addTilesetImage('tiles2');
bgLayer = map.createBlankLayer('background', tileset2);
bgLayer.fill(0);
let fgLayer = map.createLayer(0, tileset, 0, 0);
// Just to show that the Background is still show if not Tile is covering
fgLayer.removeTileAt(0, 0);
fgLayer.removeTileAt(1, 0);
fgLayer.removeTileAt(2, 0);
player = this.add.rectangle(point1.x, point1.y, 5, 5, 0xffffff, .5)
.setOrigin(.5);
this.input.on('pointerdown', () => {
isLeaking = !isLeaking;
this.infoText.setText( `Click to toggle leaking: ${isLeaking?'off':'on'}` )
updateMap(map);
});
updateMap(map);
}
function updateMap (map) {
let originPoint1 = map.getTileAtWorldXY(point1.x, point1.y);
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
);
let bgTile = bgLayer.getTileAt(tile.x, tile.y, false)
let hideOnlyTheseTiles = [ 0, 1, 2, 3, 4]; // Indexes to hide
if( !isLeaking ){
if(hideOnlyTheseTiles.indexOf(bgTile.index) > -1){ // here yopu can select the
bgTile.setAlpha(0);
}
} else{
bgTile.setAlpha(1);
}
tile.setAlpha(1 - 0.09 * dist);
});
}
<script src="//cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>

Phaser issue, all my tiles have null properties when selected

I've been working on a project found here -> https://paperambi.glitch.me/
as seen in console, all my tiles are recorded null, and therefore i cant get their index property, what is wrong with my tiles, and is there anyway to fix it?
Heres an example, when you click on tiles to get properties, its null
and if you add index, it responds with cannot find null of index.
Am I using the wrong function, or have something wrong with my tiles?
code sandbox -> https://codesandbox.io/s/31xpvv85om?hidenavigation=1&module=/js/index.js&moduleview=1&file=/js/index.js:0-3327
all code for the example
/**
* Author: Michael Hadley, mikewesthad.com
* Asset Credits:
* - Tileset by 0x72 under CC-0, https://0x72.itch.io/16x16-industrial-tileset
*/
import Phaser from "phaser";
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: "game-container",
backgroundColor: "#1d212d",
pixelArt: true,
scene: {
preload: preload,
create: create,
update: update
}
};
const game = new Phaser.Game(config);
let controls;
let marker;
let shiftKey;
let groundLayer;
function preload() {
this.load.image(
"tiles",
"../assets/tilesets/0x72-industrial-tileset-32px-extruded.png"
);
this.load.tilemapTiledJSON(
"map",
"../assets/tilemaps/platformer-simple.json"
);
}
function create() {
const map = this.make.tilemap({ key: "map" });
const tiles = map.addTilesetImage(
"0x72-industrial-tileset-32px-extruded",
"tiles"
);
// Same setup as static layers
map.createLayer("Background", tiles);
groundLayer = map.createLayer("Ground", tiles);
map.createLayer("Foreground", tiles);
shiftKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SHIFT);
// Set up the arrows to control the camera
const cursors = this.input.keyboard.createCursorKeys();
const controlConfig = {
camera: this.cameras.main,
left: cursors.left,
right: cursors.right,
up: cursors.up,
down: cursors.down,
speed: 0.5
};
controls = new Phaser.Cameras.Controls.FixedKeyControl(controlConfig);
// Limit the camera to the map size
this.cameras.main.setBounds(0, 0, map.widthInPixels, map.heightInPixels);
// Create a simple graphic that can be used to show which tile the mouse is over
marker = this.add.graphics();
marker.lineStyle(5, 0xffffff, 1);
marker.strokeRect(0, 0, map.tileWidth, map.tileHeight);
marker.lineStyle(3, 0xff4f78, 1);
marker.strokeRect(0, 0, map.tileWidth, map.tileHeight);
// Help text that has a "fixed" position on the screen
this.add
.text(
16,
16,
"Arrow keys to scroll\nLeft-click to draw tiles\nShift + left-click to erase",
{
font: "18px monospace",
fill: "#000000",
padding: { x: 20, y: 10 },
backgroundColor: "#ffffff"
}
)
.setScrollFactor(0);
}
function update(time, delta) {
controls.update(delta);
// Convert the mouse position to world position within the camera
const worldPoint = this.input.activePointer.positionToCamera(
this.cameras.main
);
// Place the marker in world space, but snap it to the tile grid. If we convert world -> tile and
// then tile -> world, we end up with the position of the tile under the pointer
const pointerTileXY = groundLayer.worldToTileXY(worldPoint.x, worldPoint.y);
const snappedWorldPoint = groundLayer.tileToWorldXY(
pointerTileXY.x,
pointerTileXY.y
);
marker.setPosition(snappedWorldPoint.x, snappedWorldPoint.y);
// Draw or erase tiles (only within the groundLayer)
if (this.input.manager.activePointer.isDown) {
if (shiftKey.isDown) {
groundLayer.removeTileAtWorldXY(worldPoint.x, worldPoint.y);
} else {
groundLayer.putTileAtWorldXY(353, worldPoint.x, worldPoint.y);
var tile = groundLayer.getTileAt(
worldPoint.x,
worldPoint.y
); //.index;
console.log(tile);
}
}
}
The problem is the function you are using getTileAt (link to the documentation), with world coordinates, there are two easy solutions:
you could use your variable pointerTileXY like:
var tile = groundLayer.getTileAt(
pointerTileXY.x,
pointerTileXY.y
);
Or you could use the function getTileAtWorldXY (link to the documentation)
var tile = groundLayer.getTileAtWorldXY(
worldPoint.x,
worldPoint.y
);

Sync Object3d rotation (tab level)

Need help,
I'm trying to sync the rotation of one object loaded in the active browser tab with another object loaded in the second tab. While rotating Object loaded in the active browser tab will send a message to another. In the second tab will listen to the message sent from the first tab and rotate the Object accordingly using rotation values. my code looks like below, (I referred this example)
let objectLoaded;
let previousMousePosition = {x : 0, y : 0 };
function mouseDown(e) {
isDragging = true;
}
function mouseUp() {
isDragging = false;
if (objectLoaded) {
// publishing the rotation value to another tab using 'hermes' package.
hermes.send('rotation', JSON.stringify({
rotation: objectLoaded.rotation,
quaternion: objectLoaded.getWorldQuaternion(),
matrix: objectLoaded.matrixWorld
}));
}
}
function mouseMove(e) {
const deltaMove = { x : e.offsetX - previousMousePosition.x,y : e.offsetY - previousMousePosition.y };
if (isDragging && objectLoaded) {
const deltaRotationQuaternion = new THREE.Quaternion().setFromEuler(new THREE.Euler(toRadians(deltaMove.y * 1), toRadians(deltaMove.x * 1), 0, 'XYZ'));
objectLoaded.quaternion.multiplyQuaternions(deltaRotationQuaternion, objectLoaded.quaternion);
}
previousMousePosition = {x : e.offsetX, y : e.offsetY };
}
function toRadians(angle) {
return angle * (Math.PI / 180);
}
Listener code,
hermes.on('rotation', (rotationObj) => {
// rotationObj structure: {rotation: { _x: 0, _y: 0: _z: 0}, quaternion: { _x: 0, _y: 0: _z: 0, _w: 0}}
const target = JSON.parse(rotationObj);
SecondObject.rotateX(target.rotation._x);
SecondObject.rotateY(target.rotation._y);
SecondObject.rotateZ(target.rotation._z);
})
The issue here is the second object not properly syncing with the first one. Do any one know what i'm doing wrong?
Thanks,
In one tab, you request the current (absolute) rotation. In the other the, you rotate by those values in addition to the current rotation. That will break right after the first time you set a non-zero rotation.
What you want is to assign the rotation, by doing
SecondObject.rotation.copy(target.rotation);

Using easelJS to free transform shapes on pressmove

I want to replicate the basic functionality of a free transform tool (no rotation), by dragging on the border of a easeljs Shape and adjusting the container to match it. I'm currently using the scaleX and scaleY properties and it sort of works but is not quite right.
If you do one scaling transformation it works pretty well. However if you release, then do another scaling transformation, it jumps very glitchily, and can occasionally break sending the x/y coordinates all the way to stage 0. Any help on this issue would be great!
http://jsfiddle.net/frozensoviet/dsczvrpw/13/
//circle
var circle = new createjs.Shape(new createjs.Graphics()
.beginFill("#b2ffb2")
.drawCircle(0, 0, 50));
circle.setBounds(0, 0, 50, 50);
//create the border as a seperate object
var cBorder = new createjs.Shape(new createjs.Graphics().setStrokeStyle(10)
.beginStroke("#000").drawCircle(0, 0, 50));
cBorder.setBounds(0, 0, 50, 50);
//add both to the container
circleContainer.addChild(circle);
circleContainer.addChild(cBorder);
var cWidth = circleContainer.getBounds().width;
var cHeight = circleContainer.getBounds().height;
//find initial mouse position relative to circle center
cBorder.on("mousedown", function (evt) {
//initial mouse pos
this.initial = {
x: Math.abs(-(circleContainer.x - evt.stageX)),
y: Math.abs(circleContainer.y - evt.stageY)
};
});
//set the relevant circle axis scale to ratio of mouse pos/initial mouse pos
cBorder.on("pressmove", function (evt) {
//current moouse pos
this.offset = {
x: Math.abs(-(circleContainer.x - evt.stageX)),
y: Math.abs(circleContainer.y - evt.stageY)
};
if (this.initial.x > this.initial.y) {
//sides
circleContainer.scaleX = this.offset.x / this.initial.x;
} else if (this.initial.x < this.initial.y) {
//top/bottom
circleContainer.scaleY = this.offset.y / this.initial.y;
} else {
//diagonals
circleContainer.scaleX = this.offset.x / this.initial.x;
circleContainer.scaleY = this.offset.y / this.initial.y;
}
stage.update();
});
The issue is your initial calculations don't account for the change in the scale of the circle. You would have to transform the coordinates using localToGlobal. Fortunately, there is an even easier way:
this.initial = {
x: Math.abs(evt.localX),
y: Math.abs(evt.localY)
};
You can also turn on ignoreScale on the border, which makes it not stretch:
createjs.Graphics().setStrokeStyle(10,null,null,null,true) // The 5th argument
Lastly, your bounds setting might work for your demo, but it is not correct. Your circle draws from the center, so it should be:
cBorder.setBounds(-25, -25, 50, 50);
Here is an updated fiddle: http://jsfiddle.net/tfy1sjnj/3/

Trying to draw multiple level scene in html5 canvas

this is my jsfiddle : jsfiddle.net/V8eKp/5/show
// START -- MOVE CAMERA
var p = new b2Vec2();
p = (ball.body.GetWorldCenter().x) * physics.scale;
pos.push(p);
var length = pos.length;
var s = (pos[length - 1] - pos[length - 2]); //in pixels
if ((halfwidth < (dw - p)) && (p > halfwidth)) {
ctx.translate(-s, 0);
}
I followed the code in here : http://www.codingowl.com/readblog.php?blogid=128
the rendering of the ball is acting weirdly and slow
i saw this example : http://www.emanueleferonato.com/2010/05/04/following-a-body-with-the-camera-in-box2d-the-smart-way/ but i didn't figure out how he do it he used 3 variables stage,x,y wish are undefined in the code he wrote.
and check out this demo by Impactjs and look how the camera move with the player movement i need this functionality : http://impactjs.com/demos/physics/
please can anyone help me in this
As long as ball.body.GetPosition().x > 48, this.current will start incrementing on each update, right? But your draw() only handles 0, 1 and 2.
Got a (kind of) working fix here: http://jsfiddle.net/u4Mv5/
You already got most of it right, just need a bit of work on the scene change logic.
My logic is, whenever the scene change, remove everything, manually shift the ball back or forward, and completely redraw the new scene.
As we are manually shifting the ball, the canvas does not need to be translated.
Draw and loop code: (Simplified)
draw: function () {
this.clear();
new Body(physics, { name: "Ground" });
ball = new Body(physics, { shape: "circle", x: ballx, y: bally, radius: 0.5 });
switch ( this.current ) {
case 0: // First scene
new Body(physics, { name: "Scene0Wall" });
new Body(physics, { name: "Scene0bricks0" });
new Body(physics, { name: "Scene0bricks1" });
break;
case 1: // Second scene
new Body(physics, { name: "Scene1bricks0" });
break;
case 2: // Third scene
new Body(physics, { name: "Scene2bricks0" });
break;
}
},
update: function () {
ballx = ball.body.GetPosition().x;
bally = ball.body.GetPosition().y;
if ( ballx > scene_width ) {
++this.current;
ballx -= scene_width;
this.draw();
}
if ( ballx < 0 && this.current > 0) {
--this.current;
ballx += scene_width;
this.draw();
}
},
This is also easier on scene layout since each scene's origin is [0,0].
I'm only preserving the balls' position, not its state and velocity, which you should implement. The scene layout (and scene cut off point) should also be adjusted to make sure the ball can continue its journey instead of getting stuck in a block.

Categories