s&box/field-guide
the symptom, in your words

"Disabling per-instance shadows on ModelRenderer"

✓ verified on engine 26.07lane: Getting art inposted
▸ SYMPTOM

You try to disable shadow casting on a ModelRenderer with renderer.CastShadows = false and get a CS1061 compile error. The property appears in Sandbox.Engine.xml metadata, but it's not a public settable property on ModelRenderer.

▸ CAUSE

Only ParticleModelRenderer.CastShadows is a public settable property. The ModelRenderer type does not expose a direct CastShadows setter. The shadow toggle lives one layer deeper, on the SceneObject that the renderer creates internally.

▸ FIX

Toggle shadows through SceneObject.Flags:

snippet
var r = go.Components.Create<ModelRenderer>();
r.Model = Model.Load( "models/scatter/grass_tuft.vmdl" );

// SceneObject is valid immediately after Create (populated in OnAwake)
if ( r.SceneObject.IsValid() )
{
    r.SceneObject.Flags.CastShadows = false;
}

Key points:

  • SceneObject is a get-only property populated synchronously during the renderer's OnAwake, so it's valid right after Components.Create<ModelRenderer>().
  • Guard with IsValid() defensively — it should always be true after creation, but protects against edge cases.
  • The SceneObjectFlagAccessor type (verified writable via Sandbox.Engine.dll reflection under net10.0) exposes CastShadows as a real read-write property.
  • Essential for large scatters (1000+ instances like grass tufts) where shadow maps are pure GPU cost with no visual payoff.
▸ WHY IT WORKS

The engine's rendering pipeline reads shadow-casting from the SceneObject flags, not from a component-level property. Disabling it there removes the instances from shadow passes entirely — no per-frame cost, no material change needed.

Verified on engine 26.07 — seen in a real project.
s&box moves fast; an undated fix is a liability. Spot a stale detail?