Getting art in
Blender / AI model → in-game, correct scale & facing.
Two-stage flow: Blender headless emits Y-up OBJ, then pure Python writes vmats/vmdls and a C# model catalog.
At single-digit sun elevation a street canyon gets no direct ground light, so nothing on the ground casts a visible shadow while the cascade is billed in full; fix it with sun azimuth, not renderer flags.
On current builds a PointLight created with Shadows = true contributes nothing to the frame: no light, no shadow, silently. The six-face cubemap shadow path is broken while SpotLight's single-view path works. For a directional lamp, cast with a downward SpotLight instead.
A primitive authored with a rotation parameter spins about its own object origin, wherever it was placed, not about the part's intended pivot. A raked hood then see-saws about its own middle and shows half the intended wedge in each direction. Rotate the placement coordinate around the pivot first, then place the box there with the same rotation baked in, and remember that a rotated box's own bounding box is trigonometric, not its declared half-length.
A PhysicsHullFile node compiled with import_mode HullPerElement treats a material break as an element boundary. Paint two adjoining primitives with a second material and the compiler folds them into one element and builds one convex hull across both. The convex hull of a wall with a hole in it is a solid wall, so every doorway and window built the same way seals shut at once, with no geometry change in the diff. Every offline check stays green, because none of them compile the model. The defect is only visible in the compiled collision, which the editor or an in-engine walk probe can see. Fix by giving physics its own single-material copy of the source mesh, so the render mesh's material list never reaches the physics node, and pin the physics mesh to the render mesh with a lint.
bpy.ops.wm.obj_export can reorder face lines across runs on the same mesh: diff the manifest, not the raw OBJ bytes.
A brand-new custom .shader committed without its compiled .shader_c fails its first in-editor load with a misleading 'Invalid Dependency Information' / file-not-found error chain, even when the HLSL is correct. The engine loads shaders only from .shader_c -- commit both.
Census tris × instances first: silhouette fails before UVs; inject usemtl after scripted OBJ export.
Scaling DirectionalLight.LightColor does not dim the sun: from 0.001 to 5.0 it renders identically; only Color.Black turns it off. LightColor sets hue, not brightness. A day/night brightness ramp built on it is a silent no-op.
ModelRenderer.CastShadows doesn't exist as a settable property: use renderer.SceneObject.Flags.CastShadows instead.
complex.shader has no shipped source and no evidence it reads vertex color as albedo, so don't plan per-vertex color painting through it. The reliable runtime color-per-surface lane is a baked texture plus Material.CreateCopy and Set("Color", tex).
Forge delivery job errors out but the build stage's previewGlbUrl stays downloadable: recover the GLB through Blender into your standard delivery shape instead of re-spending.
Geometry parented under an IsStatic root samples its lit appearance at creation time and never resamples. A runtime write to a light component is invisible on that geometry until the world is rebuilt. So a lighting A/B that mutates a light live and re-observes static geometry shows no change, no matter how large the delta. The comparison reads as no effect when the real answer is never re-sampled. Run static-geometry lighting tests through the world build command, not a live component write.
A near-white chalky sky stays mid-grey or blue: fixing the sky texture alone isn't enough because the default tonemapping curve crushes highlights.
The render node in a vmdl honours import_rotation. PhysicsHullFile and PhysicsMeshFile ignore it. So correcting an off-axis import with import_rotation fixes the picture and leaves the collision exactly where it was. The model builds clean with no error and no warning, photographs correctly, and a player walks straight through it. Bake a corrected engine-frame copy of the mesh that both the render node and the physics node read, rather than rotating one consumer with a property the other ignores.
CC0 kits import like any OBJ+vmdl; city kits share colormap.png, nature kits need per-color vmats, and facing is a per-model yaw guess.
ModelRenderer.Tint is packed to eight bits per channel and the cast wraps instead of clamping, so a tint above 1.0 renders dark, not brighter. Rendered luminance follows (tint * 255) mod 256, so 1.02 drops to about 56 and 1.10 reads as 24 of 255. The channels do not wrap together, so a population jittered around 1.05 splits into flat red, yellow, and near-black frames that keep their texture detail and read like an atlas or UV fault. Normalise the authored tint ladder so the largest draw reaches white, and clamp at every sink where a colour becomes a renderer component.
RenderMeshFile alone compiles clean with zero physics: ModelCollider needs PhysicsMeshFile/PhysicsHullFile in the vmdl.
Blender +X becomes world −Y after OBJ import; yaw +90° faces +X, and Blender +Y rotation tips the +X edge down.
CapsuleCollider and ModelCollider ignore WorldScale; BoxCollider follows it. Bake scale into import_scale instead.
A PhysicsShapeList node compiles with several tagged PhysicsHullFile children, so one vmdl can carry a compound collision volume. The per-shape tags stay inert through a scene ModelCollider.
A multi-hull vmdl authored with per-shape collision_tags does not get those tags at runtime -- a scene ModelCollider overwrites every shape with the GameObject's own tags, so Scene.Trace.WithoutTags can't filter one hull from another. Split the hulls across separate collider GameObjects instead.
1 m = 39.37 engine units: design in SI, convert once at the engine boundary, and audit every consumer.
Scenes never load raw OBJ/GLB: always wrap with a .vmdl and remap materials under both bare and .vmat names.
The engine ships its own render quality profiles, backed by real convars, that CAP what your scene asked for. A scene authoring 4 shadow cascades renders 2 for a Low-shadow player. And post-processing (AO, DOF, bloom, tonemapping, SSR) is opt-in per camera: none of it runs until the game adds the component, so a code-created camera renders flat, ungraded, and un-occluded no matter how well the lights are authored.
A prop that reads as flat, plastic, or untextured almost always has a texture. The mip chain destroyed its contrast before the player saw it, because the detail was authored finer than the scale the surface is seen at.
A hard SkyBox2D.SkyMaterial swap produces a visible pop, and stacking two SkyBox2D components with Tint alpha crossfade does not composite. The fix is a single custom sky shader that holds all time-slot textures and blends them by weight.
Even-time samples of a ballistic arc cluster at the slow apex and spread at the fast ends: sample by arc length for evenly spaced preview dots.
Shader field support is unknown and no .shader source exists: check the install's templates/ folder for authoritative syntax instead of grepping sibling projects.
AI mesh generators (Tripo, Meshy, Rodin) deliver textured OBJ+vmdl, but modeldoc32 headers, ~1m normalize, corrupt textures, bare texture paths, and missing colliders will ERROR the asset until patched.
Source engine convention: Forward = +X, Left = +Y, Right = -Y. Using Vector3.Right for '+X' silently slides geometry the wrong way.