Chapter 12

Bridging Worlds — When Two Engines Disagree

poqpoq imports content from OpenSim and Second Life. Both pipelines render the same geometry — but not the same way. The story of how one tiny convention disagreement cascaded through 1,500 columns, and the architectural discipline that finally stopped it.

poqpoq's build tool: torus shapes rendered with the host engine's conventions, alongside a CREATE panel with Box / Sphere / Cylinder / Torus tabs
poqpoq's build tool
Source-world viewer: equivalent torus shapes with a classic SL/OpenSim object panel showing prim parameters like path cut, hollow, twist, taper, and revolution
Source-world viewer (SL / OpenSim)
The same torus, two different pipelines. Same parameters. Same intent. But every disagreement between the engines — axis orientation, profile winding, default convention — is a place where a tiny correction could be applied.

The Problem

Second Life and OpenSim ship one parametric mesh generator. When their viewer renders a torus, when their physics engine meshes it, when their export tool dumps it to disk — every layer reads vertex layout, axis orientation, and profile direction from the same source. There is no second pipeline that produces a torus differently and then needs reconciling.

poqpoq is built on a different engine entirely. Its parametric shape generators have their own reasonable defaults — for "which axis is up," for which direction profiles wind, for whether a default torus stands upright or lies flat. These defaults are internally consistent. They just aren't the same as the source-world's. Every disagreement is a place where a small correction could be applied to make the visible shape match.

The Cascade

The first time you write that correction, it looks local. A single shape, a single prim, one rotation tweak, and the visible donut renders correctly. The fifth time, it is a pattern. The fiftieth time, something has happened to the orientation field on every imported object: it has stopped meaning "where the user wanted this facing" and started meaning "where the user wanted this facing, plus whatever geometry corrections accumulated along the way."

That distinction sounds academic until the day a builder imports 1,500 Doric columns into a sim and watches all of them tilt.

1,500+ Columns Affected
1 Root Cause
7+ Downstream Consumers
4 Iteration Rounds
A top-down view of an imported world: a plaza with palms, columns, and architecture, where many of the column elements are visibly tilted 90 degrees off vertical, lying on their sides instead of standing upright
The bug in production. Imported columns visibly lying on their sides instead of standing upright — the 90° cascade, propagated through every linkset that had a torus capital as its reference frame.

Why columns? Doric columns are composed of small linked pieces — a base, a shaft, a capital. The capital is a torus. When the torus's orientation field carries an injected correction, and the column inherits that torus's frame as its reference, every child piece of the column gets the same correction applied to it. One Post-it stuck on one field, multiplied across an entire imported world.

flowchart TD SRC[Source-world export] SRC -->|injects +90° correction| FIELD[Orientation field
on the imported object] FIELD --> R1[Geometry renderer
reads as rotation] FIELD --> R2[Linkset frame
reads as reference] FIELD --> R3[Scripts
read as facing] FIELD --> R4[Gizmo display
reads as user orientation] FIELD --> R5[Federation export
reads as portable transform] R2 --> CHILD1[Column shaft tilts] R2 --> CHILD2[Column base tilts] R2 --> CHILD3[Other linkset members tilt] classDef src fill:#1e1e1e,stroke:#0099ff,color:#0099ff,stroke-width:2px classDef field fill:#2a2a2a,stroke:#f44336,color:#f44336,stroke-width:2px classDef reader fill:#1e1e1e,stroke:#404040,color:#e0e0e0 classDef child fill:#2a2a2a,stroke:#ff9800,color:#ff9800 class SRC src class FIELD field class R1,R2,R3,R4,R5 reader class CHILD1,CHILD2,CHILD3 child
One correction. Five readers. Three cascade victims per linkset. The orientation field carries information meant for one consumer but consumed by all of them.

Where The Post-it Got Stuck

The bug was a cascade because the orientation field had two readers who fundamentally disagreed about what it meant. The geometry pipeline read it as "rotate this shape this much." The linkset system read it as "this is the user's reference frame, so everything attached to this object positions relative to it." When the field carries only user intent, both readers agree. When the field carries user intent plus a geometry-correction quietly injected at import time, the readers split, and the children of the rotated parent come along for the ride.

Fixing the visible tilt would have meant patching every reader. The real fix was somewhere else entirely: in the geometry pipeline, before anything reached the orientation field.

"SL and OpenSim don't have a Babylon to fight."
— the architectural mantra that emerged from the cluster

Linden Lab built the entire viewer pipeline themselves. There is no second engine inside their system that disagrees with the first about how a torus should be oriented — so their orientation field can carry pure user intent, and every consumer downstream can trust that reading. The field is clean because nothing in their pipeline contaminates it.

poqpoq has a different rendering engine. Every place where its defaults disagree with the source-world's defaults is a place where a correction could land. The mantra answers where: the correction belongs in the place where vertices are made — baked into the geometry itself — not in the orientation field that seven other systems read. Vertex baking is inside the geometry pipeline; what gets baked there cannot leak. Orientation fields sit between systems; what lands there cascades through every reader.

The Two-Layer Paradigm

The mantra surfaced an architectural framing that turned a single bug-cluster fix into a durable design contract. Every parametric shape is really two independent things, and those things must never share a field:

Layer What it is Who owns the convention
Shape The vertex positions, baked at generation time, frozen. The geometry pipeline.
Placement Where the user put it. How the user oriented it. The user.

Shape carries the canonical-orientation convention internally: an identity-rotation torus looks like a barrel because the vertices were baked that way, not because a placement correction made it so. Placement carries pure user intent — what the build tool emitted when the user dragged the gizmo, what the import source said the user had oriented the object as.

Shape Layer
Vertex positions, baked at generation time, frozen. Convention disagreements with the source engine resolve here.
bridge
lives here
Placement Layer
The user's orientation. Read by the linkset frame, scripts, the gizmo, federation, exports.
When the bridge lives in Shape: downstream readers stay clean.
linkset frame ✓ scripts ✓ gizmo ✓ federation ✓ re-export ✓
Shape Layer
Vertex positions, generated with the host engine's defaults. Convention disagreement not resolved here.
Placement Layer
User orientation plus a hidden correction injected at import time. Field is no longer pure user intent.
+90°
stuck here
When the bridge lives in Placement: every reader inherits the contamination.
linkset frame ✗ scripts ✗ gizmo ✗ federation ✗ re-export ✗
The same disagreement, fixed in two different layers. The Shape-layer fix is invisible to every downstream system; the Placement-layer fix is consumed by all of them.

The moment a geometry correction lands in Placement, every downstream consumer that reads Placement inherits the contamination. The linkset frame inherits it. Scripts that ask "what direction is this object facing?" inherit it. The gizmo's reference-axis display inherits it. Re-exporting the world to another platform inherits it. Federation across other worlds inherits it.

The opposite is also true: keeping Placement clean unlocks every consumer to behave correctly by default. The fix did not have to visit each consumer. It only had to visit the place where the contamination was being introduced.

The Diagnostic Question

Architectural mantras are useful when they have an operational form you can use mid-debug. This one does:

"Where did the Post-it get stuck — Shape layer or Placement layer?"
The diagnostic you ask before fixing any cross-engine convention symptom.

When you find a Post-it in the Placement layer — a rotation tweak conditional on prim type, a per-shape axis swap, a special-case offset added at export time — strip it. Push it into vertex generation. The downstream cascade goes with it. The question is small enough to be a habit, and big enough to close every bug in this class.

A handoff modal titled 'Send to pOqpOq World', showing a scene about to be exported with 7,002 meshes, 426 materials, 250.4 MB, and 355 textures, with buttons to 'Stay in Legacy' or 'Go to World'
The cross-tool handoff in production. A 250 MB scene with seven thousand meshes flows from the import tool into the world. The contract that makes this safe is exactly the kind of seam this chapter is about: each side owns its own conventions, and the bridge between them lives in a single explicit place.
The same plaza after the fix: Doric columns standing upright, palms swaying, sky and water rendered correctly, a builder avatar walking through the scene
The same plaza after the geometry-layer fix. Columns standing upright, no Post-it stuck in the orientation field, no downstream consumers contaminated. The bridge moved into Shape; Placement got its meaning back.

Why This Matters Beyond One Bug

Every system that imports from another system faces this question somewhere. Game engines that import models from other modeling tools negotiate axis conventions. Physics engines that bridge between simulators negotiate units. Networking layers that bridge two protocols negotiate byte order. The pattern is universal: when two internally-consistent systems meet, every disagreement is a place where a correction could be applied at the wrong layer.

1

Locate the disagreement.

What does the source system promise? What does our system deliver by default? The disagreement might be one axis, one sign, one byte order — tiny individually, expensive in the wrong place.

2

Identify the readers.

Who downstream reads the field where the correction could land? If only one reader exists, a transform-layer correction is harmless. If five readers exist, the correction contaminates all five of them.

3

Push the correction inward.

Move the bridge into the layer that is inside your own pipeline — the layer where what gets baked cannot leak. The cascade through downstream readers disappears when its source does.

4

Make the convention explicit.

Document the canonical orientation for every shape. Future you (or the next developer) needs a lookup table answering "what does identity-rotation produce for this shape?" so the next analogous problem never has to be rediscovered empirically.

What's Next

This chapter handled one cross-engine bridge. The next two chapters handle two more architectural disciplines that emerged from the same family of bug-clusters — both of them constitutional rules rather than local fixes.

Chapter 13 The architecture of persistence: how a virtual world keeps its data consistent when more than ten code paths can create the same kind of object — and why "parallel tracks that look unified but live in separate worlds" is the sister problem to the bridge.
Chapter 14 The no-trap principle: a design rule for any user interface that hides controls behind progressive disclosure — and the cascade of related principles it surfaced in a single design session.