I'm trying to figure this out for a while now and I just can't find a solution.
I'm working with three JS and the Cannon Library.
I created a simple object/ mesh with a tween animation. With the Cannon Library I added a physical body to the mesh.
I put an animation an the mesh so it changes the rotation but the rotation of the physical body just doesn't change at all.
second position
First position
This is where I define the physics:
class Physics {
constructor() {
this.world = new CANNON.World();
this.stepSize = 0;
this.timeToGo = 0;
this.visualObjects = [];
this.physicalBodies = [];
}
addPair(visualObject, body) {
this.visualObjects.push(visualObject);
this.physicalBodies.push(body);
}
initialize(gravityX, gravityY, gravityZ, stepsize, addfloor) {
this.world.gravity.set(gravityX, gravityY, gravityZ);
this.world.broadphase = new CANNON.NaiveBroadphase();
his.stepSize = stepsize;
if (addfloor) {
var floor = new CANNON.Body({
shape: new CANNON.Plane(),
mass: 0
});
floor.quaternion.setFromAxisAngle(new CANNON.Vec3(1, -0, 0), -Math.PI / 2);
floor.position.y = -32;
this.world.addBody(floor);
}
}
update(delta) {
// Step physics world forward
this.timeToGo += delta;
while (this.timeToGo >= this.stepSize) {
this.world.step(this.stepSize);
this.timeToGo -= this.stepSize;
}
// Copy transformations
for (var i = 0; i < this.visualObjects.length; i++) {
this.visualObjects[i].position.copy(this.physicalBodies[i].position);
this.visualObjects[i].quaternion.copy(this.physicalBodies[i].quaternion);
}
}
getWorld() {
return this.world;
}
addStandPhysical(visualObject, mass){
var shapeAblage = new CANNON.Box(new CANNON.Vec3(0.5*2,0.5*8,0.5*40));
var shapeMittelstrebe = new CANNON.Box(new CANNON.Vec3(0.5*10,0.5*1,0.5*100));
var shapeAblage3 = new CANNON.Box(new CANNON.Vec3(0.5*2.5,0.5*1,0.5*15));
var shapeAblage4 = new CANNON.Box(new CANNON.Vec3(0.5*2.5,0.5*1,0.5*15));
var shapeFuss5 = new CANNON.Box(new CANNON.Vec3(0.5*2.5,0.5*1,0.5*15));
var shapeMittelding = new CANNON.Cylinder(2,2,25,32)
var body = new CANNON.Body({ mass: mass });
body.addShape(shapeAblage, new CANNON.Vec3(-1.25, 29, 41), new CANNON.Quaternion().setFromEuler( 0, 90*DEG_TO_RAD,90*DEG_TO_RAD, "XYZ")); //ablage1
body.addShape(shapeAblage, new CANNON.Vec3(-1.25, 29, -41), new CANNON.Quaternion().setFromEuler( 0, 90*DEG_TO_RAD,90*DEG_TO_RAD, "XYZ")); //ablage2
body.addShape(shapeAblage, new CANNON.Vec3(-1.25, -29, -41), new CANNON.Quaternion().setFromEuler( 0, 90*DEG_TO_RAD,90*DEG_TO_RAD, "XYZ")); //ablage3
body.addShape(shapeAblage, new CANNON.Vec3(-1.25, -29, 41), new CANNON.Quaternion().setFromEuler( 0, 90*DEG_TO_RAD,90*DEG_TO_RAD, "XYZ")); //ablage4
body.addShape(shapeMittelstrebe, new CANNON.Vec3( 1.75, 0, 0), new CANNON.Quaternion().setFromEuler(35* DEG_TO_RAD, 0, 0, "XYZ"));
body.addShape(shapeMittelstrebe, new CANNON.Vec3( -1.75, 0, 0), new CANNON.Quaternion().setFromEuler(-35* DEG_TO_RAD, 0, 0, "XYZ"));
body.position.copy(visualObject.position);
body.quaternion.copy(visualObject.quaternion);
this.world.addBody(body);
this.addPair(visualObject, body);
}
}
And this is basically my main:
function main() {
scene = new THREE.Scene();
physics = new Physics();
physics.initialize(0, -200, 0, 1 / 120, floor = false); //Hier mit der Stepsize vielleicht noch ein wenig experimentieren und herausfinden, was sie macht
physicsVisualDebugger = new THREE.CannonDebugRenderer(scene, physics.getWorld());
var stand = new Stand();
physics.addStandPhysical(stand,3);
scene.add(stand);
var clock = new THREE.Clock();
function mainLoop() {
stats.begin();
var delta = clock.getDelta();
physics.update(delta);
physicsVisualDebugger.update();
stand.animations.forEach(function (animation) {
animation.update(delta);
});
TWEEN.update();
renderer.render(scene, camera);
stats.end();
requestAnimationFrame(mainLoop);
}
}
So long question short: how do I get the physical Bodies to move with my animation of the meshes to the ground?
I consider but maybe I am wrong, that your two arrays
this.visualObjects, this.physicalBodies
have not the same length. So maybe the update loop do not copy right the positions and quaternions. Better use the loop this way:
world.bodies.forEach( function(body) { ... }
to get looped exact the bodies.
///
var stencilWrite = material.stencilWrite;
stencilBuffer.setTest( stencilWrite );
if ( stencilWrite ) {
stencilBuffer.setMask( material.stencilWriteMask );
stencilBuffer.setFunc( material.stencilFunc, material.stencilRef, material.stencilFuncMask );
stencilBuffer.setOp( material.stencilFail, material.stencilZFail, material.stencilZPass
);
}
///
The above is the threejs code. After commenting, the following template function of my code can run normally. I do n’t know why? It seems that the value of material.stencilWrite and state.buffers.stencil.setTest (true) in maskPass are in conflict? When rrenderer.render (this.scene, this.camera); in the pass, run stencilBuffer.setTest (stencilWrite); in the above code again?
///my code
var renderPass = new THREE.RenderPass(this.__scene, this.camera);
renderPass.renderToScreen = true;
renderPass.clearDepth = true;
this.effectComposer.addPass(renderPass);
var renderSelectPass = new THREE.RenderPass(this.selectScene, this.camera);
renderSelectPass.clear = true;
this.effectComposer.addPass(renderSelectPass);
var maskPass = new THREE.MaskPass(this.selectFace, this.camera);
maskPass.clear = true;
maskPass.renderToScreen = true;
this.effectComposer.addPass(maskPass);
///
It can run normally before the v106 version.
When I create a sprite, OrbitControls works perfectly.
But when I switch to FirstPersonControls, everything freezes and the console gives this error:
Uncaught TypeError: Cannot read property 'matrixWorld' of undefined
at Sprite.raycast (three.js:26051)
at intersectObject (three.js:44583)
at Raycaster.intersectObjects (three.js:44658)
at animate (mainview.php?project_id=46&tipo=1:936)
Raycasting code:
if (firstorbit) {
if (l % 5 == 0) {
var vector = new THREE.Vector3(0, -1, 0);
vector = camera.localToWorld(vector);
vector.sub(camera.position);
var raycasterGround = new THREE.Raycaster(camera.position, vector);
var intersectsGround = raycasterGround.intersectObjects(scene.children, true);
if (intersectsGround.length > 0) {
if (intersectsGround[0].distance < 1.21) {
camera.position.y = (camera.position.y + (1.21 - intersectsGround[0].distance));
}
if (intersectsGround[0].distance > 1.36 && intersectsGround[0].distance < 1.45) {
camera.position.y = (camera.position.y - (intersectsGround[0].distance - 1.215));
}
}
}
if (l % 3 == 0) {
var vector1 = new THREE.Vector3(0, 0, -1);
vector1 = camera.localToWorld(vector1);
vector1.sub(camera.position);
var raycasterFront = new THREE.Raycaster(camera.position, vector1);
var intersectsFront = raycasterFront.intersectObjects(scene.children, true);
if (intersectsFront.length > 0) {
if (intersectsFront[0].distance < 0.3) {
controls.moveForward = false;
controls.w = false;
} else {
controls.w = true;
}
} else controls.w = true;
}
}
Raycasting code that I use on mouse click to measure something:
function getIntersections( event ) {
var vectorMouse = new THREE.Vector2();
vectorMouse.set(
mouse.x,
mouse.y );
var raycasterClick = new THREE.Raycaster();
raycasterClick.setFromCamera( vectorMouse, camera );
var intersectsClick = raycasterClick.intersectObjects( scene.children,true );
return intersectsClick;
}
I am not sure if the problem is with the sprite or with my raycasting code. But when I remove the sprite, everything works perfectly. And when I remove the Raycasting code, everything also works perfectly.
I'm using 'dat.gui' for user input and want the tableBoardGeometry and the tableLegsGeometry to update in my render method with the values from the input.
Geometric declarations
const boxWidth = 2;
const boxHeight = 0.1;
const boxDepth = 1;
const tableBoardGeometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
const tableLegPosition = (boxWidth * 0.45);
const tableLegHeight = 1.6
const tableLegHeightPosition = (tableLegHeight/2)
const tableLegsGeometry = new THREE.BoxGeometry(0.05, tableLegHeight, 0.05);
Dat.Gui
var gui = new dat.GUI();
var controls = function() {
this.TableWidth = 1;
this.TableHeight = 1;
this.LegsWidth = 1;
this.LegsHeight = 1;
this.RotationSpeed = 0.005;
}
var title = new controls();
gui.add(title, 'RotationSpeed', 0.005, 0.1);
Render Method
const render = () => {
requestAnimationFrame(render);
tableBoard.rotation.y -= title.RotationSpeed;
/* I'd like to update the Geomtrics here depending on user input */
renderer.render(scene, camera);
orbitCamera()
}
I've tried using:
tableBoard.scale.x = title.TableWidth;
and it doesnt work properly because of the variable dependencies for getting the right number. The variable that i'd like to update is tableLegHeightPosition
edit:
The issue is that the sub mesh follows the parent mesh on scaling
I'm using Xamarin.Forms + Urhosharp, I've got a problem to set texture from an image on a sphere. The problem is that texture.Load or texture.SetData always returns false. I did try different methods like SetData, Load, resize texture and image (to a power of 2 number) and ... but none of them worked. Here is my code:
private async void CreateScene()
{
Input.SubscribeToTouchEnd(OnTouched);
_scene = new Scene();
_octree = _scene.CreateComponent<Octree>();
_plotNode = _scene.CreateChild();
var baseNode = _plotNode.CreateChild().CreateChild();
var plane = baseNode.CreateComponent<StaticModel>();
plane.Model = CoreAssets.Models.Sphere;
var cameraNode = _scene.CreateChild();
_camera = cameraNode.CreateComponent<Camera>();
cameraNode.Position = new Vector3(10, 15, 10) / 1.75f;
cameraNode.Rotation = new Quaternion(-0.121f, 0.878f, -0.305f, -0.35f);
Node lightNode = cameraNode.CreateChild();
var light = lightNode.CreateComponent<Light>();
light.LightType = LightType.Point;
light.Range = 100;
light.Brightness = 1.3f;
int size = 3;
baseNode.Scale = new Vector3(size * 1.5f, 1, size * 1.5f);
var imageStream = await new HttpClient().GetStreamAsync("some 512 * 512 jpg image");
var ms = new MemoryStream();
imageStream.CopyTo(ms);
var image = new Image();
var isLoaded = image.Load(new MemoryBuffer(ms));
if (!isLoaded)
{
throw new Exception();
}
var texture = new Texture2D();
//var isTextureLoaded = texture.Load(new MemoryBuffer(ms.ToArray()));
var isTextureLoaded = texture.SetData(image);
if (!isTextureLoaded)
{
throw new Exception();
}
var material = new Material();
material.SetTexture(TextureUnit.Diffuse, texture);
material.SetTechnique(0, CoreAssets.Techniques.Diff, 0, 0);
plane.SetMaterial(material);
try
{
await _plotNode.RunActionsAsync(new EaseBackOut(new RotateBy(2f, 0, 360, 0)));
}
catch (OperationCanceledException) { }
}
Please help!
To Create a material from a 2D Texture, you can use Material.FromImage .
Refer following documentation for more detail.
model.SetMaterial(Material.FromImage("earth.jpg"));
https://developer.xamarin.com/api/type/Urho.Material/
https://developer.xamarin.com/api/member/Urho.Material.FromImage/p/System.String/
private async void CreateScene()
{
_scene = new Scene();
_octree = _scene.CreateComponent<Octree>();
_plotNode = _scene.CreateChild();
var baseNode = _plotNode.CreateChild().CreateChild();
var plane = _plotNode.CreateComponent<StaticModel>();
plane.Model = CoreAssets.Models.Sphere;
plane.SetMaterial(Material.FromImage("earth.jpg"));
var cameraNode = _scene.CreateChild();
_camera = cameraNode.CreateComponent<Camera>();
cameraNode.Position = new Vector3(10, 15, 10) / 1.75f;
cameraNode.Rotation = new Quaternion(-0.121f, 0.878f, -0.305f, -0.35f);
Node lightNode = cameraNode.CreateChild();
var light = lightNode.CreateComponent<Light>();
light.LightType = LightType.Point;
light.Range = 100;
light.Brightness = 1.3f;
int size = 3;
baseNode.Scale = new Vector3(size * 1.5f, 1, size * 1.5f);
Renderer.SetViewport(0, new Viewport(_scene, _camera, null));
try
{
await _plotNode.RunActionsAsync(new EaseBackOut(new RotateBy(2f, 0, 360, 0)));
}
catch (OperationCanceledException) { }
}