← Paracosm·Docs/Handbook
Contents · Textures and surfaces

Textures and surfaces

Encoding and uploading

1. Encode J2C with MCT=0

Encode textures as a raw JPEG 2000 codestream with NO multi-component (colour) transform: MCT=0, 5/3 reversible wavelet, 6 resolution levels, starting FF 4F. upload_texture refuses anything that does not start FF 4F.

2. Keep textures square

3. Cache uploads by content hash

A re-upload makes a new asset UUID. Cache the UUID keyed on a hash of the file content, not its modification time.

4. Prove the UV path on a trivial object

tools/make_uv_testrect.py builds a 4 x 2 x 1 orientation box (hue = axis, brightness = sign) whose six faces map by per-vertex UV into six labelled atlas cells, through exactly the spawn_mesh path real models use. If the box reads right in the viewer, the UV pipeline is right.

Putting textures on faces

5. Box prim face numbers

0 top, 1 -Y, 2 +X, 3 +Y, 4 -X, 5 bottom, in the prim's own axes. On a side face the image's top is the top of the wall.

6. Texture a prim per face from a one-shot script

PRIM_TEXTURE, face, uuid, <u_rep, v_rep, 0>, <0, 0, 0>, 0 with repeats worked out from the prim's real size, so a stone is the same size on every wall. A script that ends with llRemoveInventory(llGetScriptName()) leaves the look and no script behind. Set the tint to white so the texture shows its own colour.

7. A mesh takes one texture at spawn; faces are tinted from LSL

spawn_mesh texture= puts one texture on every face. Per-face COLOUR from LSL after spawn reaches the viewer (face = submesh). So: a pale texture plus face tints (lacquer red, bronze), or an atlas whose bands are symmetric in v so it reads right whichever way the viewer counts v.

8. Show one cell of an atlas with repeats and offset, not a one-frame animation

A one-frame texture animation (llSetTextureAnim(ANIM_ON | LOOP, face, 4, 2, 4.0, 1.0, ...), meant to hold one cell) is not held by Firestorm: the face showed the whole atlas. Set the face's repeats and offset instead: PRIM_TEXTURE, face, tex, <1/cols, 1/rows, 0>, <(c + 0.5)/cols - 0.5, (1 - (r + 0.5)/rows) - 0.5, 0>, 0. Layer a multi-frame animation on top only where motion is wanted.

9. Detail that sits at a fixed place on a part belongs in that part's UV frame

A lofted hull reads smooth unless each plank is flat across its width, and a dark hull still hides its laps in the viewer's flat light. Put the lap in the TEXTURE: map each strake to one full v tile (v 0 = lower edge) so a shadow band, a lit chamfer and a row of rivets fall where they belong on every strake. The same for deck planks (u = the plank's whole length, so nail pairs land over the stringers; four plank variants stacked in v) and cask staves (one stave per u tile, joints dark at both edges).

10. Draw a card's texture in the card's own proportions

A card's texture cell is square but the card is not (a 0.66 m wing span on 64 px, a 0.2 m chord on 128 px). Draw the shape in the card's coordinates (fill the chord), or it becomes a sliver the alpha mask shreds to specks at any distance.

11. Image rows run down, UV v runs up

Build image arrays with y rising, then np.flipud before saving.

Growing textures

12. Grow tileable textures instead of using the default plywood

The default plywood under a tint reads as "bare faux woodgrain plywood". Grow tileable textures: FFT-filtered noise (irfft2 of k^-beta shaped white noise, band-limited by kmin/kmax) is periodic by construction; a toroidal Voronoi gives rubble stone.

13. Set coverage by percentile, and scale features to the texture

Threshold a normalised field at a percentile, not a fixed value: a mixed field's tails are never reached, and a lichen silently vanished. Work out the texel size (at 1.1 m per repeat, 1 px is about 1 mm, so a 3-8 cm lichen rosette is 30-80 px), band-limit to that or it shatters to speckle, and multiply by a low-frequency "where it grows" field or it spreads evenly like sprinkles.

14. A regular grid reads as wallpaper

Coursed rubble grown in the texture wants courses per tile, stones of random length wrapping so it tiles, per-stone tone, pillowed edges, deep joints and a low warp on the joints so they are not ruled lines.

15. Planar mapping magnifies contrast

Planar projection tiles the atlas over the object. For a mottled-noise skin it is indistinguishable from UV mapping, but keep texture contrast LOW and detail FINE: bold warped cracks planar-magnified into op-art. Prefer per-vertex UV mapping for any texture whose placement matters.

Alpha

16. Put change in the tint and the mask cutoff; put variety in the texture (the alpha ladder)

A face's colour and alpha-mask cutoff are scriptable (PRIM_COLOR, PRIM_ALPHA_MODE, face, PRIM_ALPHA_MODE_MASK, cutoff). Bake the variety into the texture: leaves pale and near-neutral (the tint is the season) and every leaf its OWN alpha on a shuffled ladder (70..245). Raising the cutoff then drops leaves one at a time from every cluster (a crown that thins, not fades); lowering it makes them arrive (litter that gathers).

17. A world-space field can live in a texture's alpha

Map a texture ONCE over a whole area (u, v = world x, y over a square) and make the alpha ladder a world-space order, and the mask cutoff advances a front across the area. The ice sheet's ladder is true distance from the shore (plus smooth noise), so the ice creeps in from the banks and closes over the deep middle last.

18. Read a mask cutoff back through its material id

inspect shows only a face's materialID, but the id is a content hash: the same mode and cutoff always give the same id. Calibrate a table once on a scratch box, then read any face's cutoff off its id. Fast-forward a seasonal script and read colour and cutoff per face as it runs; that is the proof, not the script compiling.

Relief, shine and colour under the viewer's light

19. Normal maps from the texture itself

LSL can set a face's normal map (PRIM_NORMAL). Derive it from what the texture already knows (the alpha where a leaf is, a vein, dark fissures in bark), blur a little, and write a tangent-space map (RGB = (n + 1) / 2; +x right, +y up, +z out of the face). spawn_mesh normal= wires one at spawn.

20. Specular: small environment shares in bright rooms

A glossy face with an environment share mirrors the room.

21. Daylight whitens light greys

The viewer whitens a light grey in daylight. Paint pale surfaces darker than they should look.

22. PBR materials are carried but not scriptable here

This OpenSim carries per-face RenderMaterials to the viewer, but YEngine does not implement PRIM_GLTF_*; setting them would need a new WorldBridge command. Blinn-Phong (diffuse, normal, specular) was sufficient for this world.