Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable adding new ContactMaterial to World #64

Closed
codynova opened this issue Apr 5, 2020 · 8 comments
Closed

Enable adding new ContactMaterial to World #64

codynova opened this issue Apr 5, 2020 · 8 comments
Labels
enhancement New feature or request good first issue Good for newcomers

Comments

@codynova
Copy link
Member

codynova commented Apr 5, 2020

Adding multiple ContactMaterials to the World is useful for unique contact equation properties between two specific Materials

const material1 = new CANNON.Material({ name: 'ElasticMaterial1', friction: 0, restitution: 1 })
const material2 = new CANNON.Material({ name: 'ElasticMaterial2', friction: 0.2, restitution: 0.9 })
const contactMaterial = new CANNON.ContactMaterial(material1, material2, {})
world.addContactMaterial(contactMaterial)
world.addBody(new CANNON.Body({
    // ...props
    material1
}))
world.addBody(new CANNON.Body({
    // ...props
    material2
}))
@codynova codynova added enhancement New feature or request good first issue Good for newcomers labels Apr 5, 2020
@mattrossman
Copy link

I found this issue because I need the ContactMaterial functionality for setting up different bounciness between objects. I see it's marked good first issue, how would I go about solving it? I'm struggling to picture how the syntax would look on the r3f side.

@codynova
Copy link
Member Author

codynova commented Oct 9, 2020

I think we basically need some sort of dictionary of ContactMaterials inside the worker. If we allow ContactMaterials to be passed as part of a new hook, the worker can check if that material is already in the dictionary - and then add it to the world object as appropriate.

See comment below

@mattrossman
Copy link

A ContactMaterial requires two Materials as parameters, but it looks like useBody is what produces each Material. How would you pass the ContactMaterial into useBody without first having references to the Materials upon which it depends?
It almost seems to me like you'd have to return a material from each hook and then have some other function that you pass those materials into, e.g.

const [planeRef, planeApi, planeMaterial] = usePlane(...)
const [sphereRef, sphereApi, sphereMaterial] = useSphere(...)
useContactMaterial(planeMaterial, sphereMaterial, { restitution: 1 })

@codynova
Copy link
Member Author

You're right, I didn't review my original post closely enough and misspoke. But you actually create the materials that you pass into the useBody hooks:

const material1 = {...}
const material2 = {...}
const [ref1, api1] = useBox(() => ({ material: material1, ... }))
const [ref2, api2] = useBox(() => ({ material: material2, ... }))
useContactMaterial(material1, material2, { ...options })

@mattrossman
Copy link

That's a good point. So in that case material1 holds the dictionary of options rather than the Material itself. How would this behave if material1 and material2 have the same contents (but are intended to be different materials in the contact materials)? You mentioned keeping a dictionary of materials, so I'm wondering what will be the unique keys. I know that a Material in cannon can have an optional name or id, maybe we need to make one of those required?

I also wonder how it behaves when the objects are empty. Personally, I have no use for the Material properties, I only care about ContactMaterials properties, so I'd be inclined to keep the objects empty. It seems a bit strange to be creating a bunch of variables to hold empty objects (perhaps another argument for making the name or id a requirement).

@codynova
Copy link
Member Author

Two Materials are required to create a ContactMaterial, but if I remember correctly the only Material property that the ContactMaterial cares about is the id. Cannon sets the ids of Materials and ContactMaterials inside their respective classes using a static counter. I don't think it's a good idea to change the id generation in the Cannon classes.

So I think maybe we need a useMaterial hook that allows passing the Material constructor options. We then return a "material" object with the created Material's props. Then you could pass that material object into the useBody hooks, and the useContactMaterial hook. The useMaterial and useContactMaterial hooks can just run on the first render only, so each hook only creates a single Material - then we won't end up needing dictionaries at all.

const elasticMaterial = useMaterial(() => ({ name: 'PerfectlyElastic', friction: 0, restitution: 1 }))
const otherMaterial= useMaterial()
const [ ref, api ] = useBox(() => ({ material: elasticMaterial }))
const contactMaterial = useContactMaterial(elasticMaterial, otherMaterial, options)

@Glavin001
Copy link
Contributor

Glavin001 commented Dec 22, 2021

I have an open PR adding ContactMaterial support and a Friction example: #316
Would appreciate any code reviews!

Edit: New PR: #317

@Glavin001
Copy link
Contributor

Update: #317 has been merged, adding the feature.
You can test it now here: 🎉

@bjornstar is polishing up the worker and example code in #328 before publishing. 🚀

Thanks all for your interest and contributing! The previous discussions and PRs helped make #316 happen.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request good first issue Good for newcomers
Projects
None yet
4 participants