In this example:
https://glitch.com/~query-aframe-cameras
I have registered a component which launches a projectile in the direction the user is looking (with a little boost for elevation)
Spacebar or Screen Tap to launch - be sure to be looking above the horizon!
It fails in mobile vr (stereo camera) mode:
Projectiles continue to fire, but from the default orientation of the mono, not the stereo camera
I'm using:
var cam = document.querySelector('a-scene').camera.el.object3D;
var camVec = new THREE.Vector3();
var camDir = cam.getWorldDirection(camVec);
to get the camera information and spit the projectiles back
QUERY:
HOW DO I GET THE STEREO CAMERA INFORMATION
The problem will be due to this bug in THREE.js.
https://github.com/mrdoob/three.js/issues/18448
Following the documented workaround, the code here works with correct direction tracking on my Quest 2.
https://cuddly-endurable-soap.glitch.me
(there's a couple of other changes in here: to test this on my VR headset I also had to upgrade to A-Frame 1.1.0, and make the code fire the balls on a timer, since I don't have any keys to press when using it! Neither of these was enough to fix the problem, though).
This code will set up CamDir as you need it.
var camQ = new THREE.Quaternion();
cam.updateMatrixWorld();
camQ.setFromRotationMatrix(cam.matrixWorld);
var camDir = new THREE.Vector3(0, 0, 1);
camDir.applyQuaternion(camQ);
The following code also works (and is more efficient), but is less easy to understand.
var camDir = new THREE.Vector3();
var e = cam.matrixWorld.elements;
camDir.set(e[ 8 ], e[ 9 ], e[ 10 ]).normalize();
I picked it up from here:
https://lace-fern-ghost.glitch.me/
Which I found a link to from here:
https://github.com/aframevr/aframe/issues/4412
(but I can't find any reference to explain why these components matrixWorld.elements are the right ones to use here to get the direction).
Related
I am starting to work with Spark AR studio and I looking for to get the screen size in pixel to compare the coordinate obtained by the gesture.location on Tap.
TouchGestures.onTap().subscribe((gesture) => {
// ! The location is always specified in the screen coordinates
Diagnostics.log(`Screen touch in pixel = { x:${gesture.location.x}, y: ${gesture.location.y} }`);
// ????
});
The gesture.location is in pixel (screen coordinate) and would like to compare it with the screen size to determine which side of the screen is touched.
Maybe using the Camera.focalPlane could be a good idea...
Update
I tried two new things to have the screen size:
const CameraInfo = require('CameraInfo');
Diagnostics.log(CameraInfo.previewSize.height.pinLastValue());
const focalPlane = Scene.root.find('Camera').focalPlane;
Diagnostics.log(focalPlane.height.pinLastValue());
But both return 0
This answer might be a bit late but it might be a nice addition for people looking for a solution where the values can easily be used in script, I came across this code(not mine, forgot to save a link):
var screen_height = 0;
Scene.root.find('screenCanvas').bounds.height.monitor({fireOnInitialValue: true}).subscribe(function (height) {
screen_height = height.newValue;
});
var screen_width = 0;
Scene.root.find('screenCanvas').bounds.width.monitor({fireOnInitialValue: true}).subscribe(function (width) {
screen_width = width.newValue;
});
This worked well for me since I couldn't figure out how to use Diagnostics.log with the data instead of Diagnostics.watch.
Finally,
Using the Device Info in the Patch Editor and passing these to the script works!
First, add a variable "to script" in the editor:
Then, create that in patch editor:
And you can grab that with this script:
const Patches = require('Patches');
const screenSize = Patches.getPoint2DValue('screenSize');
My mistake was to use Diagnostic.log() to check if my variable worked well.
Instead use Diagnostic.watch():
Diagnostic.watch('screenSize.x', screenSize.x);
Diagnostic.watch('screenSize.y', screenSize.y);
Screen size is available via the Device Info patch output, after dragging it to patch editor from the Scene section.
Now in the open beta (as of this post) you can drag Device from the scene sidebar into the patch editor to get a patch that outputs screen size, screen scale, and safe area inserts as well as the self Object.
The Device patch
The device size can be used in scripts using CameraInfo.previewSize.width and CameraInfo.previewSize.height respectively. For instance, if you wanted to get 2d points representing the min/max points on the screen, this'd do the trick.
const CameraInfo = require('CameraInfo')
const Reactive = require('Reactive')
const min = Reactive.point2d(
Reactive.val(0),
Reactive.val(0)
)
const max = Reactive.point2d(
CameraInfo.previewSize.width,
CameraInfo.previewSize.height
)
(The point I want to emphasize being that CameraInfo.previewSize.width and CameraInfo.previewSize.height are ScalarSignals, not number literals.)
Edit: Here's a link to the documentation: https://sparkar.facebook.com/ar-studio/learn/documentation/reference/classes/camerainfomodule
I have a webVR project that used resetPose to reset the origin of the scene but apparently is now deprecated. I used it to reset the camera, so the user would look at the center of the scene again.
I assume this function isn't too hard to replicate: you either have to rotate the scene or the camera to the new origin. However I'm not that experienced with webVR or more importantly THREE.js.
I've tried to lookAt the center of the scene with the camera but I think the problem is that the webVR has control over it so I can't just move it.
Example initialisation of camera and scene
// Create a three.js camera.
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 10000);
camera.name = "Perspective Camera";
var group = new THREE.Group();
group.name = "Camera Group";
group.rotateY(Math.PI);
group.add(camera);
group.position.set(0, configuration.sphereRadius, 0);
group.permanent = true;
scene.add(group);
// Apply VR headset positional data to camera.
controls = new THREE.VRControls(camera);
According to this issue immersive-web/webxr droped support for resetPose method. They claim that devices have hardware button to force resetting pose or reset pose should be handled by application. Three.js don't have such method.
Here is simple but dirty way to reset pose (recenter, reset camera) by exiting VR and entering it again.
async function recenter() {
if (renderer.xr.isPresenting) {
let session = renderer.xr.getSession();
let buttonVR = document.getElementById("VRButton");
await session.end();
buttonVR.click();
}
}
I'm using Babylon.js 2.4.0.
I have a mesh (in the shape of a couch) loaded from a .obj file, and a camera set up like this:
let camera = new BABYLON.FreeCamera('camera1', new BABYLON.Vector3(0, 2, 0), scene);
camera.checkCollisions = true;
camera.applyGravity = true;
camera.ellipsoid = new BABYLON.Vector3(1, 1, 1);
camera.attachControl(canvas, false);
camera.speed = 0.5;
camera.actionManager = new BABYLON.ActionManager(scene);
I want to set up an event so that when I walk through the couch, "intersection" is logged to the console:
let action = new BABYLON.ExecuteCodeAction(
{ trigger: BABYLON.ActionManager.OnIntersectionEnterTrigger, parameter: { mesh: couchMesh }},
(evt) => {
console.log("intersection");
}
);
this.camera.actionManager.registerAction(action);
When I walk through the mesh, nothing is logged to the console.
I've created an example on the Babylon.js Playground using an example that they provide to check that it wasn't a problem with my mesh or camera set up, and it doesn't appear to be (the playground doesn't work either).
A camera in Babylon.js has no action manager, so even if you set one it won't really work.
To get this to work using action managers, you could define an invisible box around the camera, with a predefined size and attach the action manager to the mesh created. then set the mesh's parent to be the camera, and you are done. Here is your playground with those changes - http://www.babylonjs-playground.com/#KNXZF#3
Another solution is to use the internal collision system of babylon js, and set the camera's onCollide function to actually do something :) Here is en example - http://www.babylonjs-playground.com/#KNXZF#4
Notice that in the second playground, the camera won't go throug the box, as the collision system prevents it from doing so. I am not sure about your usecase, so it is hard to say which one of the two will work better.
If you need a "gate" system (knowing when a player moved through a gate, for example), use the 1st method. The 2nd is much cleaner, but has its downsides.
I have two boxes. One spawns on the ground and the other is dropped on top of it. Gravity is turned on. I am trying to get the collision event listener to fire on the bottom box which is resting on the ground. However, nothing is logged.
var c = new Physijs.BoxMesh( new THREE.CubeGeometry( 5, 5, 5 ), new THREE.MeshBasicMaterial({ color: 0x888888 }) );
c.__dirtyPosition = true;
c.position.set(10, 0,-5);
c.addEventListener('collision', function(object){
console.log("hello world"); // NOT FIRING
});
scene.add(c);
var p = c.clone();
p.__dirtyPosition = true;
p.position.y = 50;
scene.add(p);
I can't figure out what I'm doing wrong - could it be because of __dirtyPosition?
EDIT: I tested it without clone() and creating the second box anew, but it doesn't make a difference.
EDIT 2: It's worth mentioning that the simulation runs fine I just can't get the listener to work.
Relevant GitHub Issue
It looks like the clone method is part of THREEjs, not Physijs. So your code just clones the physical material, not the physical mesh. You're just going to have to create another BoxMesh using the same code you did for c.
I was toying with socket.io, ThreeJS, Javascript, and NodeJS to create a simple client/server using ThreeJS's graphics. I wasn't sure if all of these frameworks would even work together, but I decided to give it a shot since I've seen similar examples online before even though I cannot find a simple one to dissect or experiment with. It's mainly to experiment with, but I also wanted to make a small little concept-game as proof of what I've learned so far.
I posted my code here: https://gist.github.com/netsider/63c414d83bd806b4e7eb
Sorry if it's a little untidy, but I did my best to make it as readable as possible.
Basically, right now the server-side NodeJS script seems to run fine (Run with "node server-alpha.js"), and the client script (client-alpha.html, which you can just open in a browser) connects to the server, and displays a list of users (who are also connected). However, my intention was for each user to be able to move his/her own cube around, and right now each cube only gets added to the screen (rather than being added, subtracted, and then added again - to give the illusion of movement). If you run both pieces of code and connected one or two users and move the arrow keys a few times for each, you'll see what I'm talking about.
Can anybody help me with this? I tried several different ways to remove the cube (and remembered to call render(), after each)... but everything I tried didn't seem to work. It always resulted in the cubes just being added to the screen, and never subtracted.
I added comments in the code to make things a little easier, as I know this is quite a bit of code to go through (if it's not your own, anyway).
Thanks, any help would be greatly appreciated... as I'm really stuck trying to make the cubes just move.
Also, I'm having trouble adding the Fly-Controls (FlyControls.js - it's commented out ATM), so if someone could tell me where I went wrong I'd appreciate that a lot also.
Ok so you don't want to keep remaking the cubes, all you need to do is change the position.
Also in game development, it is almost a requirement to use object oriented design, a good way to go about this would be to make a player object, so..
CPlayerList = new Array(); // an array of player objects
function ClientPlayer()
{
this.Cube;
this.Name = "unnamed";
this.Id = 0;
this.Create = function(name,pos,id)
{
this.Name = name;
this.Id = id;
var cubeGeometry = new THREE.BoxGeometry(10, 10, 10);
var cubeMaterial = new THREE.MeshLambertMaterial({color: 'red', transparent:false, opacity:1.0});
this.Cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
this.Cube.position.x = pos.x;
this.Cube.position.y = pos.y;
this.Cube.position.z = 20; // don't know why this is 20, remember y axis is up & down in opengl/threejs
scene.add(this.Cube);
}
this.Move = function(vector)
{
this.Cube.position.set(this.Cube.position.x + vector.x, this.Cube.position.y + vector.y, 20);
}
}
So on the server you need a ServerPlayer object which holds similar data, and assign ids on the server before sending them to the clients. So when you send it to the client you want to make a new ClientPlayer, call player.Create() and then push it to the CPlayerList, like so:
function newCPlayer(data)
{
var newPly = new ClientPlayer();
newPly.Create(data.name,data.pos,data.id);
CPlayerList.push(newPly);
}
Then when you call your movePlayer() function, you can simply loop through your players array
function movePlayer(keyStroke, clientID)
{
if (keyStroke == 39)
{
CPlayerList.forEach(function(player,i,a)
{
if(player.Id === clientID)
{
player.Move(new THREE.Vector3(1,0,0));
}
}
}
}
This is just the client code, but this should help you get started, let me know if there's anything you're unclear on.
Also here's an example of a game using a similar design: http://82.199.155.77:3000/ (ctrl+shift+j in chrome to view client sources) and server code: http://pastebin.com/PRPaimG9