The coat engine / thirteen loci
Eye colour & heterochromia
A horse’s eyes are thirteen ordinary heritable loci, and this is the single source of truth for all of them. Iris colour, sclera colour, and the two halves of sectoral heterochromia — once per eye. Two glows per eye. A third eye in the middle of the forehead. No gene paints an iris on its own account any more: the ones that used to ask for an allele, and the allele is written onto the foal at birth.
Every colour, sector and glow below is asserted through the real pipeline in
EyeColorTest. What is unknown is whether a four-texel iris split in
two reads as heterochromia on a horse at riding distance; whether the two
diagonal sectors read as anything but stray pixels; whether the named sectors come
out on the right side of both eyes (the un-mirroring is derived from the sheet’s
documented column order, not from a render); and where exactly the third eye lands
on the forehead. Checklist on To be verified.
Why the eyes are loci and not a channel
They were a channel: a gene implemented EyeColorContribution,
returned a colour it had invented, and the composer applied the highest-ranked claim.
Champagne owned four shades, tiger eye owned two, the four white loci shared a blue.
That made “what colour are its eyes” a question you answered by finding out
which gene won.
It is backwards. A horse’s eye colour is a heritable trait of the horse, and a breeder should be able to aim at it, carry it, and lose it in a cross. So the eyes are genes now, like everything else that makes a horse look the way it does, and the ranking argument about melanin is simply gene order, which is where every other ordering in this mod already lives.
The thirteen loci
Eight are natural and sit immediately after tiger eye, the gene that made the case for them. Five are magical: nothing in life glows and nothing has a third eye. Every one of them follows the same shape — one dominant wild type, and variants that are recessive to it and to each other, so a horse shows one only by carrying the same one twice. Green beside gold is a brown-eyed horse carrying two things, not a compromise between them.
| Locus | Band | Wild type | Variants |
|---|---|---|---|
| Iris colour, left / right | natural | Brown | Green, Gold, Dark blue, Mid blue, Light blue, White, Chaos, Invisible |
| Sector, left / right | natural | none | Upper left, Top, Upper right, Right, Lower right, Bottom, Lower left, Left, and the two diagonals |
| Sector colour, left / right | natural | Mid blue | Green, Gold, Dark blue, Brown, Light blue, White, Chaos |
| Sclera colour, left / right | natural | White | Black, Invisible, Chaos |
| Glowing iris, left / right | magical | no glow | Glowing |
| Glowing sclera, left / right | magical | no glow | Glowing |
| Third eye | magical | two eyes | Left imitation, Right imitation, Mix, Defined |
The keys, the priorities and the allele tokens are all in
EyeLocus, which is the one place they are written down; never quote them
here, where they would go stale. The hues are EyeHue, the sectors
EyeSector.
Left and right are separate genes
That is the whole reason complete heterochromia is something you can breed rather than wait for. But separate loci inheriting independently would make a mismatched pair as common as the square of the variant rate on each side, and every odd-eyed horse in the world would be a wild-caught accident.
So the second eye’s founder table is conditioned on the first:
most founders draw the same combination on both sides, and the rest roll freely. The
share is AbstractEyeGene.TWIN_MATCH_PERCENT. Nothing else changes —
the two loci still inherit independently, so a matched founder’s foals can still
come out odd-eyed, which is exactly the door a breeder walks through. This is why every
right-eye locus sorts one priority ahead of its left-eye twin: only the locus that rolls
second can read the first.
The colour vocabulary
One closed list, shared by the iris, sector-colour and sclera loci, so that “gold” means one thing on a horse however it got there. Two entries in it are not colours:
- Chaos has no value of its own. It reads one off the epigenetic
numbers of the allele copy carrying it, so no two chaos-eyed horses match and a line
of them drifts. It is the one outcome at these loci that
varies(), which is what stops two visibly different horses sharing a cached texture. - Invisible paints nothing. Its texels are not copied back off the template at all, so the coat on the head runs straight through where the eye would be. An invisible iris takes its sector and its glow with it — a sector of an iris that is not there is not there either.
The trap, and it caught this once already. The coat is composited onto the template
by multiplying, and the template’s iris is pure black, so a
texel that is merely skipped comes out black — the template’s own eye,
indistinguishable from an ordinary one. An invisible half is therefore
recomposited against plain white before the redraw, in
CoatTextureComposer.clearInvisibleEyeTexels.
The iris is the dark texels, the sclera the light ones
On the coat sheet an adult eye is a 2×2 block of pure black — the iris and pupil together, there being no room for both at 128px — beside a 2×2 block of near-white sclera. A foal’s is the black block alone, which is why the sclera loci show nothing on a foal and everything on the same horse grown up.
So CoatOverlay.tintIris weights each texel by how dark it is on the
template, and tintSclera is its exact complement. Worth stating because the
obvious tool is the wrong one: shadeToward weights by brightness, so
pointed at an eye it colours the sclera and leaves the iris black.
Which half a texel belongs to is read off the template, never off the coat in front of it. That used to be the other way round and it was wrong the moment an iris could be invisible: a cleared iris shows the horse’s own coat, which on a pale horse is bright, and the sclera painter claimed it.
The white sclera paints nothing
The wild type of both sclera loci is white, and it deliberately does not paint. The template’s sclera is already the white of a horse’s eye, antialiased into the iris beside it; flattening it to one flat hue would throw that away on every horse in the world to say something it already said. An iris cannot do the same — the template’s is pure black, which is not an eye colour — so brown really is painted, on every horse.
Four quadrants, ten named sectors
An iris is four texels, so a sector is a four-bit mask over them:
bit 0 bit 1 upper left upper right
bit 2 bit 3 lower left lower right
The ten alleles are the four halves, the four corners and the two diagonals. The
diagonals are the two masks EyePatch.WEDGES deliberately leaves out, on the
grounds that at four texels a diagonal reads as two stray pixels rather than a wedge of
colour; they are alleles here all the same, by the owner’s call, because a sector
nobody can reach is not a shape argument, it is a missing allele.
The two eyes’ faces are mirrored on the sheet, so bit 0 is not the
same corner of the horse on both eyes. That did not matter while a wedge was a random
shape nobody had named; it matters now that a player can breed for “upper
left” and expect upper left. EyeSector.maskFor flips the mask for the
east face. Which face needs the flip is derived from the sheet’s documented
column order rather than from a render — if a bred pair comes out mirrored
in game, EyeSector.MIRRORED_EYE is the one line to change.
The sector locus gates the sector-colour locus and not the other way round: a horse wild type for the shape shows nothing whatever the colour says. That is also why the colour locus’s wild type is mid blue rather than brown — the commonest sector in the world is the blue wedge a white locus asks for, so a horse that acquires a sector without being asked about the colour gets the one people expect.
Requests: how a coat gene gets the eyes it implies
Seven natural genes have something to say about an eye. None of them paints one. Each
implements EyeRequestContribution and returns an EyeRequest
— alleles at the eye loci — and Eyes.force writes them
onto the horse when the horse is made.
| Locus | Combination | Asks for |
|---|---|---|
| MATP (cream / pearl) | Cr/prl | Green — the compound heterozygote, and the mod’s ordinary source of a green eye |
| any double dilute, or classic pearl | Light blue | |
| sunshine | Gold | |
| Champagne | any Ch | Gold, Brown or Green, rolled off its own allele copy |
| Tiger eye | any expressing | Gold |
| MITF, PAX3 (splash) | any expressing | Mid blue |
| EDNRB (frame) | any expressing | Mid blue |
| KIT | broad white and up | Mid blue — a sabino with four socks and a blaze has ordinary dark eyes |
A splashed white foal really is MBl/MBl at both iris loci. It shows blue
eyes and passes blue eyes on, splash or no splash. That is the
owner’s call and it is the whole difference between this and an override: blue
spreads through a herd the way any other allele does.
When two genes both ask
Requests are collected in Genes.codeOrder() and merged
last-writer-wins per locus. A cremello that is also splashed has blue
eyes because KIT and MITF come after SLC45A2, not
because anything ranks them. The old three-rank argument about melanin — whole-body
dilution beaten by an iris-specific gene beaten by depigmentation — is exactly the
order those genes already sit in, so it survives without a fourth number to maintain.
One blue eye, and the blue wedge
One MITF copy produces “anything from subtle facial white or one blue
eye to extensive splash”. Complete heterochromia and sectoral heterochromia are the
same failure of melanocyte colonisation as two blue eyes, stopped earlier, and
that is not an allele question. So it is still the EyeSpread roll it always
was — one roll, off the requesting locus’s own allele copy, deliberately not
one per white locus — with the same weighting:
| Outcome | Share of blue-eyed horses | What it asks for |
|---|---|---|
| Both whole | EyeSpread.BOTH_EYES |
Mid blue at both iris loci |
| One whole | EyeSpread.ONE_EYE |
Mid blue at one iris locus — the other eye keeps whatever the horse’s own loci gave it |
| A wedge | the remainder | a sector allele plus a blue sector colour, leaving the iris itself alone |
What is new is where the answer goes. All three come out as alleles the horse carries, so a splash-bred wedge is inheritable, which the painted mask never was.
The “broadly white however it got there” rule
A horse white from two mild alleles stacking has the same claim on a blue eye as one white from a single bold allele, and no per-locus test can see that. That rule used to measure the finished coat: the fraction of mapped texels left with no pigment at all.
It cannot any more. A request is applied when the horse is bred, and there is no
coat at that point. So it is a declared sum instead: each locus that takes pigment off a
horse says how much, through WhitePatternEyes.WhiteExtent, and the threshold
is WhitePatternEyes.WHITE_SCORE_THRESHOLD. The numbers are the painters’
own strength constants wherever there is one to borrow. It is an approximation
and is recorded as one on Known gaps.
The third eye
The one eye locus with no side, and the only one that has to invent an eye rather than describe one the horse already has. There is no “third iris colour” gene and there should not be — that would be six more loci for one rare allele — so the four alleles are the four honest answers to “and what colour is that one”:
- Left imitation / right imitation — an exact duplicate of an eye the horse already has, which is the outcome that reads as a third eye rather than as a decal.
- Mix — each part (iris, sector, sector colour, sclera, and the two glows) taken from the left or the right eye independently. A horse with two matching eyes gets a third matching one; a heterochromatic horse gets something new.
- Defined — every part written on the allele copy, drawn from the same palettes the real loci offer, so a defined third eye is never something a horse could not have inherited.
Mix and defined live on the copy, so both are inherited and both drift, like
everything else epigenetic here. Both declare varies().
Where it is drawn
The middle of the head’s top face — the forehead — and emphatically not its front face. The head box is 6×5×7 and the muzzle box 4×5×5 sits directly in front of it at the same height, so the head’s front face is covered except for a one-unit strip down either side; a third eye painted there would be invisible from every angle worth looking from.
It is four texels by two, laid out sclera / iris / iris / sclera, and it is painted
absolutely rather than tinted: the forehead is plain white on both
templates and there is no iris there to walk toward a colour.
CoatRegions.thirdEyeRect is derived from
HorseSkinGeometry’s own box-UV tables and
has not been seen in game. Unlike the two real eyes there is nothing on
the template at that spot to check it against.
The glows
Four loci, two per eye, and they have no colour of their own on purpose: the iris glows in whatever colour the iris locus settled, the sclera in whatever the sclera locus settled. That is the whole point of separating them — a gold glowing eye and a chaos-coloured glowing eye are the same allele on two different horses, so a breeder who has found the glow can then go and change what colour it glows.
Iris and sclera are separate loci because they are very different faces: a lit iris is a lamp, and a lit sclera with a dark hole in the middle of it is a dhampir. Glowing nothing is nothing — an invisible half carries the allele and shows none of it.
The magical genes that still paint eyes directly
Four hand-written Java genes predate the loci and still overrule them, painting in the overlay phase after the eye loci have run. They are not a pattern to copy, and a gene file cannot do what they do at all.
| Gene | What it does to an eye | Could the loci do it? |
|---|---|---|
| Dhampir | Red iris at EyeColor.RANK_MAGICAL, plus glowing scleras — on
the carrier as well as the homozygote |
Partly. The glowing sclera is now an allele; the red is not — red is the one iris colour in the mod with no allele behind it. |
| Shadowcreature | shadeEyes gold over the whole eye, with no white left round it, and
the whole eye emissive |
Partly. Gold iris and a glowing iris are both alleles; “the sclera is gold too” is not — the sclera palette has no gold. |
Light (Lteye) |
Gold, glowing eyes | Yes — gold iris plus both glows. The allele is now redundant as an eye effect; the locus is not, since it also does hooves and mane. |
| Magic sectoral heterochromia | Two colour alleles, one wedge of each apiece, a different shape in each eye | Yes, entirely. The sector and sector-colour loci do this per eye, with a named shape, inheritably. See the roadmap. |
One natural gene also still touches an eye: the leopard complex lifts the sclera toward a cool tint, which is what makes an appaloosa’s white-rimmed eye. It is deliberately left alone, because it is not an iris colour and there is no allele that means “more of the white shows”.
What it costs the coat cache
The eyes are drawn into the baked coat texture, so every eye locus is in the horse’s
texture key — their expressing outcomes are real marker()s rather than
wild types, and two horses that differ only there do not share a cached texture. Whether
that costs one entry per outcome or one per horse depends on whether the outcome varies:
every hue but chaos is fixed, and chaos, mix and defined are the three
things here that are not.
Writing a gene that has something to say about eyes
A gene file cannot, and should not try. There is no mask, op or effect that reaches an eye. If your gene needs a particular eye, ship a breed with it — a breed can pin any locus, the eye loci included — or tell players which eye alleles to breed onto it. The gene creator says the same thing in the same words.
A hand-written Java gene has one hook worth using:
| Hook | Returns | Use it when |
|---|---|---|
EyeRequestContribution | EyeRequest |
Your gene implies an eye colour — “a horse like this has eyes like that”. Answered from the genotype and epigenome alone, with no coat, because it runs when the horse is bred. |
EyeColorContribution, EyePatchContribution |
a colour, or patches | The old channel. Still live, still ranked, and now magical genes only — it is for painting something over a horse’s real eyes rather than for saying what they are. |
What is deliberately not modelled
- Champagne foals’ blue-green eyes. A real champagne foal is born blue-green and matures to amber or hazel; here it is the adult colour from birth. There is no aging in this mod for it to mature through. On the roadmap.
- Filmy and blind eyes. Cloudiness is cornea, lens or inflammation rather than iris pigment, and is not an inherited eye-colour category at all.
- Pupil shape. A real horse’s pupil is a horizontal slot. At two texels there is no room to say so.
- A red iris allele. The dhampir has one and nothing else can. Whether red joins the palette is an open question on the roadmap.