Closed nartc closed 2 years ago
Document the second and third arguments for physicBody.useXXX methods
https://v5--angular-three.netlify.app/ WIP docs is live here
I think the note at top of this page should also indicate some knowledge of threejs is needed too.
I think ComponentStore needs an more complete example of its use in a component. A link to an working example of its use would be good enough
A common problem I run into is the need to access the camera from a component to use in a raycast.
Show how a component gets access to the camera for the following example
// Project the mouse onto the movement plane
const hitPoint = this.getHitPoint(event.clientX, event.clientY, this.movementPlane, this.camera)
private getHitPoint(clientX: number, clientY: number, mesh: Mesh, camera: Camera): Vector3 | undefined {
// Get 3D point form the client x y
const mouse = new Vector2()
mouse.x = (clientX / window.innerWidth) * 2 - 1
mouse.y = -((clientY / window.innerHeight) * 2 - 1)
const raycaster = new Raycaster();
// Get the picking ray from the point
raycaster.setFromCamera(mouse, camera)
// Find out if there's a hit
const hits = raycaster.intersectObject(mesh)
// Return the closest hit or undefined
return hits.length > 0 ? hits[0].point : undefined
}
@IRobot1 I have updated Component Store documentation https://v5--angular-three.netlify.app/docs/core/component-store
Is Store documentation not clear on how to access the NgtStore
state, which contains the default Camera?
Wow, the new documentation is starting to look fantastic! Nice work!
For me, the pain point now is to understand where to define post-processing steps and how to generally post-process stuff. Would really appreciate even a basic example
@Polyterative thank you. Yes, there will be examples on postprocessing stuffs. It's just a lot of work getting there :D I'm fleshing out some Cannon stuffs so @IRobot1 can move on with porting cannon-es examples over to Angular Three which is HUGE for the community
@IRobot1 latest beta (beta.11) has the cannon package consolidated
// before
import { NgtPhysicBody } from '@angular-three/cannon/bodies';
// after
import { NgtPhysicBody } from '@angular-three/cannon';
I also added: Spring, Raycast, and RaycastVehicle
Thanks. I'll give it a try.
BTW, I agree the new documentation is an easier read.
NgtConstraintReturn was renamed to NgtPhysicConstraintReturn
NgtPhysicSpring api missing applyForce method
@IRobot1 https://github.com/pmndrs/use-cannon/blob/master/packages/cannon-worker-api/src/worker/operations/add-spring.ts#L31 seems like Cannon Worker API calls applyForce()
automatically and it does not expose Spring#applyForce
(only Body#applyForce
is exposed)
Is Store documentation not clear on how to access the
NgtStore
state, which contains the default Camera?
Store is not clear to me yet. It doesn't actually show how its injected/created within a component. All examples refer to "this.store", but where is this.store being added?
ComponentStore is also confusing. I'm not a react developer and never used NgRx store. It assumes I am so implies a lot. Its not clear why/when I need to extend from NgtComponentStore or SomeBaseClass.
So far, I've written lots of examples of ng3 components and never needed to use this. I tried injecting NgtStore into a component where I want to access camera, but its value is always undefined.
constructor(
private store: NgtStore,
) {
console.warn(this.store.get((s) => s.camera)); // undefined
}
I tried this approach too without luck.
@Component({
providers: [NgtComponentStore ],
})
export class MousePickExample {
constructor(
private store: NgtComponentStore ,
) {
console.warn(this.store.get((s) => s.camera)); // undefined
}
}
What am I missing?
Here's the code where I want to use the store to access the camera instead of passing it as in input.
@IRobot1 You've got a point there. I am very familiar with NgRx ComponentStore hence I subconsciously write docs with a lot of assumptions. My apologies! I'll think of how to make it clearer for developers who aren't experienced with NgRx.
Here's a PR that shows how to use NgtStore and NgtComponentStore https://github.com/IRobot1/ng3-cannon-template/pull/1
You can read through my comments on the PR to understand the changes better
@IRobot1 updated v5 doc with Cannon API sections.
This page https://v5--angular-three.netlify.app/docs/cannon/body explains the 2nd and 3rd arguments of NgtPhysicBody.use***
One limitation when using ng3/cannon is there's no quick way to make a mesh a static physics object. Currently have to
<ngt-mesh [ref]="leftSphere.ref" castShadow>
<ngt-sphere-geometry></ngt-sphere-geometry>
<ngt-mesh-standard-material></ngt-mesh-standard-material>
</ngt-mesh>
leftSphere = this.physicBody.useSphere(() => ({
mass: 0,
position: [0, 0, 4]
}));
Would be nice if there was a simple short-cut, where the correct body type was implicitly created with mass 0
<ngt-mesh static [position]="[0, 0, 4]" castShadow>
<ngt-sphere-geometry></ngt-sphere-geometry>
<ngt-mesh-standard-material></ngt-mesh-standard-material>
</ngt-mesh>
or
<ngt-mesh staticSphere [position]="[0, 0, 4]" castShadow>
<ngt-sphere-geometry></ngt-sphere-geometry>
<ngt-mesh-standard-material></ngt-mesh-standard-material>
</ngt-mesh>
Wouldn't it be to use type: 'Static'?
Yes. I think mass of 0 implies static. I've never explicitly set body type to Static.
I've been struggling with the terminology of effect and tapEffect. To me, better names would be monitor and task. Is there a way to create aliases for these?
For [canvas input], for the camera property it mentions you can provide your own THREE.Camera. I was hoping something like this was possible
<ngt-canvas [camera]="camera">
<ngt-camera #camera [args]="{ position: [16, 4, 0], fov: 65, near: 1, far: 100}"></ngt-camera>
Regardless, I was hoping there could be a way to set lookat location without having to write code.
I noticed this recent change with camera settings. If rotation is missing, it defaults to lookat origin. Unfortunately, setting rotation is not the same as looking at something.
It would be nice if camera options explicitly supported lookat position. That way, it could track/follow an object in the scene. Of course, setting lookat and rotation might not be clear.
For spring documentation, you can copy working example code from here
Refer to the following physics trigger example. View the browser console messages to see the event value when bodies collide. Here's the corresponding code
{
body: sn {uuid: '51694ff7-c2e6-4625-ad11-e244d98ab40c', name: 'sphere', type: 'Mesh', parent: Vd, children: Array(0), …}
collisionFilters: {bodyFilterGroup: 1, bodyFilterMask: -1, targetFilterGroup: 1, targetFilterMask: -1}
contact: {bi: sn, bj: sn, contactNormal: Array(3), contactPoint: Array(3), id: 0, …}
op: "event"
target: sn {uuid: '4e2972df-30d0-4673-861f-cd4f85bcd87f', name: 'trigger', type: 'Mesh', parent: Vd, children: Array(0), …}
type: "collide"
}
The physics body onCollideBegin and onCollideEnd event references the mesh that collided. However, I haven't figure out how to use this to get a reference to the body. When something collides, I want to have the option to create a constraint between to two bodies.
Its possible to search to V4 documentation. However, the current V5 documentation is missing the capability
In your pull request for the physics mousepick example, you mention this
// anything physics related, we might want to run outside of Angular zone to prevent Change Detection ticks
this.zone.runOutsideAngular(() => {
});
I think the document needs to be a topic that covers this and when/why change detection should be avoided.
Perhaps some best practices when using ngIf and ngFor too
@IRobot1
- You would use the two Refs to create the constraint instead of the bodies right? The surface API of angular-three/cannon does not work with Bodies directly but the Ref of the object. Or am I missing something?
I would have expected the ColledBeginEvent to be like the following since its bodies interacting, not meshes
export declare type CollideBeginEvent = {
body: NgtPhysicsBodyReturn;
op: 'event';
target: NgtPhysicsBodyReturn;
type: 'collideBegin';
};
In the example below, when the sphere enters the box trigger, the sphere becomes connected to the particle via the constratint
sphere = this.physicBody.useSphere(() => ({
mass: 1,
}));
particle = this.physicBody.useParticle(() => ({
mass: 0,
}));
box = this.physicBody.useBox(() => ({
isTrigger: true,
onCollideBegin: (event) => {
const constraint = this.physicConstraint.useDistanceConstraint(this.particle.ref, new Ref(event.body), {
distance:0
});
},
}));
However, the new Ref(event.body)
is not the same as sphere.ref.
As a result, I'm not able to access sphere.api. Any thoughts on how this could work? Can CollideBeginEvent be changed to pass NgtPhysicsBodyReturn?
Without this, I would need to maintain a dictionary/map of uuid to NgtPhysicsBodyReturn
Hope this makes sense.
@IRobot1 why can't you use this.sphere.ref
?
onCollide*
event returns the Refs but again, it cannot be BodiesImagine my hand is the box trigger and there's many physics bodies in the room that my hand can overlap.
Closing my hand, creates the constraint and causes the body to follow my hand until I release it. I won't know which body given the current info in the event.
Its surpising that the CollideBeginEvent refers to meshes and not bodies.
Is there an internal map of body ids to bodies?
I created the following drop in wrapper for NgtPhysicBody. It creates a map of ref uuid for all the bodies created. It includes an extra method getBody that allows the uuid in the CollideBeginEvent to lookup the body in order to create the constraint
cubeProps = this.physicBody.useBox(() => ({
isTrigger: true,
onCollideBegin: (event: CollideBeginEvent) => {
const body = this.physicBody.getBody(event.body.uuid);
if (body) {
const constraint = this.physicConstraint.useDistanceConstraint(this.particle.ref, body.ref, {
distance: 0
});
this.particle.api.position.set(0, 0, -5)
}
},
}));
constructor(
private physicBody: PhysicsBodyRegistry,
private physicConstraint: NgtPhysicConstraint,
) {
}
import { Injectable } from "@angular/core";
import { Ref } from "@angular-three/core";
import { BoxProps, CompoundBodyProps, ConvexPolyhedronProps, CylinderProps, GetByIndex, HeightfieldProps, NgtPhysicBody, NgtPhysicBodyReturn, ParticleProps, PlaneProps, SphereProps, TrimeshProps } from "@angular-three/cannon";
@Injectable()
export class PhysicsBodyRegistry {
private uuidbodymap = new Map<string, NgtPhysicBodyReturn>([]);
getBody(uuid: string): NgtPhysicBodyReturn | undefined {
return this.uuidbodymap.get(uuid);
}
removeBody(uuid: string): boolean {
return this.uuidbodymap.delete(uuid);
}
constructor(
private physicBody: NgtPhysicBody,
) { }
private register(body: NgtPhysicBodyReturn): NgtPhysicBodyReturn {
const s = body.ref.subscribe(next => {
if (next != null) {
this.uuidbodymap.set(next.uuid, body);
s.unsubscribe();
}
});
return body;
}
public useBox(fn: GetByIndex<BoxProps>, useOnTemplate?: boolean, ref?: Ref<THREE.Object3D>): NgtPhysicBodyReturn {
return this.register(
this.physicBody.useBox(fn, useOnTemplate, ref)
);
}
public useCompoundBody(fn: GetByIndex<CompoundBodyProps>, useOnTemplate?: boolean, ref?: Ref<THREE.Object3D>): NgtPhysicBodyReturn {
return this.register(
this.physicBody.useCompoundBody(fn, useOnTemplate, ref)
);
}
useConvexPolyhedron(fn: GetByIndex<ConvexPolyhedronProps>, useOnTemplate?: boolean, ref?: Ref<THREE.Object3D>): NgtPhysicBodyReturn {
return this.register(
this.physicBody.useConvexPolyhedron(fn, useOnTemplate, ref)
);
}
useCylinder(fn: GetByIndex<CylinderProps>, useOnTemplate?: boolean, ref?: Ref<THREE.Object3D>): NgtPhysicBodyReturn {
return this.register(
this.physicBody.useCylinder(fn, useOnTemplate, ref)
);
}
useHeightfield(fn: GetByIndex<HeightfieldProps>, useOnTemplate?: boolean, ref?: Ref<THREE.Object3D>): NgtPhysicBodyReturn {
return this.register(
this.physicBody.useHeightfield(fn, useOnTemplate, ref)
);
}
useParticle(fn: GetByIndex<ParticleProps>, useOnTemplate?: boolean, ref?: Ref<THREE.Object3D>): NgtPhysicBodyReturn {
return this.register(
this.physicBody.useParticle(fn, useOnTemplate, ref)
);
}
usePlane(fn: GetByIndex<PlaneProps>, useOnTemplate?: boolean, ref?: Ref<THREE.Object3D>): NgtPhysicBodyReturn {
return this.register(
this.physicBody.usePlane(fn, useOnTemplate, ref)
);
}
useSphere(fn: GetByIndex<SphereProps>, useOnTemplate?: boolean, ref?: Ref<THREE.Object3D>): NgtPhysicBodyReturn {
return this.register(
this.physicBody.useSphere(fn, useOnTemplate, ref)
);
}
useTrimesh(fn: GetByIndex<TrimeshProps>, useOnTemplate?: boolean, ref?: Ref<THREE.Object3D>): NgtPhysicBodyReturn {
return this.register(
this.physicBody.useTrimesh(fn, useOnTemplate, ref)
);
}
}
After creating a constraint, how to I delete (not disable) it at runtime?
const constraint = this.physicConstraint.useDistanceConstraint(body.ref, lastBody.ref, {
distance: distance,
maxMultiplier: this.strength, // higher multiplier makes links stronger
});
@IRobot1 I thought the returned “api” had a method to remove the constraint. It didn’t. Released beta-16 to add remove() to constraint api, spring api, and raycastvehicle api
v5 is now parity with the main
branch, as in all components have been reworked. I'll focus on documentations next then will probably promote v5 to latest version this weekend or next week.
Can you update the soba sky story example to expose the properties supported by sky component? I think this would better demonstrate the capabilities of this component.
@IRobot1
@types/three
releases the new type early enough@IRobot1 More on NgtComponentStore
, effect
, and tapEffect
The "Effect" term has been used in the frontend space for quite a while now and it means "to handle side-effect". Side-effect can be varied: fetching an API (in THREE, it can mean using any of the Loader), watching for changes on Inputs to update an object, registering a callback to the animation loop etc...
I will write documentations on what is considered an Effect in Angular Three and will link to the NgRx (inspiration) documentations as needed. The name cannot be changed because to counter your argument, there also might be people who ARE familiar with NgRx. Changing the terms would be confusing for them.
@IRobot1 added. I'll probably release v5 tonight or tomorrow.
The documentation for cannon API should mention how to use ngt-cannon-debug.
Also, recall a limitation that cannon doesn't work as expected when the mesh/body is contained in a parent that has position or rotation set. However, I can't find where this is mentioned.
<ngt-group [rotation]="[0, 1.57, 0]" [position]="[-2, 0, 0]">
<ngt-mesh [name]="'trigger'" [ref]="cubeProps.ref" [scale]="cubescale">
<ngt-box-geometry></ngt-box-geometry>
<ngt-mesh-standard-material color='white'></ngt-mesh-standard-material>
</ngt-mesh>
</ngt-group>
In the following image, the solid box is what's rendered in the scene, but the wireframe is how cannon thinks its positioned
@IRobot1 Thank you for reporting that. However, documentations is something I'd expect the community to help out. Naturally, the child object takes over the parent's object orientations if the child object does not specify its orientation. In this case, the mesh takes over the values from the Group. To workaround (I am guessing), is to pass the group's position/rotation to useBox
.
The work around is to put inside a compound body.
I'll submit a separate issue about how the current way to do this could be improved.
V5 has been released so I'll close this issue. If anyone has any suggestions for documentations, we can track em in a separate one
This is an issue used to track all Issues/regressions/breaking changes and documentation requests to v5. You can track the latest progress on documentations here: https://github.com/nartc/angular-three/tree/v5/libs/documentations/docs
In case you miss it, 5.0.0-beta.4 has been released so you can try it out and find any issues you run into. I'm in the process of writing documentations for v5.
Also,
@angular-three/soba
is missing a lot of functionalities right now as the core has changed. I will continue to fill them as I move along with the documentations.cc @IRobot1