Skip to content

GSOC 26: render multi-material models per part#8955

Open
Nixxx19 wants to merge 10 commits into
processing:mainfrom
Nixxx19:renderer-per-part
Open

GSOC 26: render multi-material models per part#8955
Nixxx19 wants to merge 10 commits into
processing:mainfrom
Nixxx19:renderer-per-part

Conversation

@Nixxx19

@Nixxx19 Nixxx19 commented Jun 24, 2026

Copy link
Copy Markdown
Member

part of my google summer of code 2026 project, "full texture support for .mtl files in p5.js". works toward #6924.

this is phase 3: the renderer side, building on the data + parser foundation in #8879. multi-material models now actually draw, one part per material, each with its own look. public api is unchanged, you still just call loadModel() and model().

project phases for context:

what's in here:

  • the renderer loops over a geometry's parts and draws each one
  • each part applies its own material (fill, specular, ambient, shininess, texture) around its draw, restored after so nothing leaks between parts
  • a single-material geometry is its own part, so that path renders exactly as before, byte for byte
  • p5.GeometryPart gains what the renderer needs to treat it as a drawable (hasFillTransparency, userVertexProperties)

tests:

  • a multi-material model (octa-color, 8 materials) draws one fill per part and passes the instance count through
  • the existing obj+mtl diffuse-colour visual test still matches its reference screenshot
  • full unit suite green, including the webgpu and visual (pixel-compare) tests

stacked on #8879, so it should go in after that one merges.

@Nixxx19 Nixxx19 changed the title render multi-material models per part GSOC 26: render multi-material models per part Jun 24, 2026
@p5-bot

p5-bot Bot commented Jun 24, 2026

Copy link
Copy Markdown

@davepagurek davepagurek left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good! I left a few comments. One thing that would be good to clarify is what the valid states of a geometry are (should they always have parts?) so that that can be an invariant in the code and simplifies implementation.

Comment thread src/core/p5.Renderer3D.js
// material around the draw.
const parts = geometry.parts && geometry.parts.length
? geometry.parts
: [geometry];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's a scenario where we'd end up with no parts?

Comment thread src/core/p5.Renderer3D.js
if (!partState) return;
if (partState.fill) {
const c = partState.fill;
this.states.setValue('curFillColor', [c[0], c[1], c[2], 1]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why it's not possible for a part to have alpha in the fill? Although the obj/mtl spec puts transparency into its own command, in p5 fills can have alpha (e.g. geometry that comes from buildGeometry would be able to have fills with alpha if a user calls something like fill(255, 100) in the middle of building it.) Maybe we can store the transparency from a mtl in the alpha field of its fill color?

suite('multi-material rendering', function() {
test('draws each part and passes the instance count through', async function() {
const renderer = myp5.createCanvas(50, 50, myp5.WEBGL);
const model = await new Promise(resolve =>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think loadModel(url) should already returns a promise, without needing to wrap it further


const spy = vi.spyOn(renderer, '_drawFills');
myp5.background(255);
myp5.model(model, 4);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The second parameter here is for WebGL instanced rendering, is that intended to be set in this test?

}

function geometryPart(p5, fn) {
p5.GeometryPart = GeometryPart;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this maybe could be done in the existing geometry addon, since there's not a way to use geometry without geometryPart currently. So this file could just export the class, and p5.Geometry can import it and add it to the p5 object

// octa-color has several materials, so several parts.
expect(model.parts.length).toBeGreaterThan(1);

const spy = vi.spyOn(renderer, '_drawFills');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should have a similar spy and assertion for _drawStrokes, which I believe we still need to implement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants