Vertex
Description
Section titled “Description”A single vertex: a required 2D or 3D position plus any per-vertex attributes your shader declares. Commonly that’s uv and color, but any key the shader’s vertex stage reads is fair game. Build vertices and feed them into a Mesh to draw.
Canonical attribute names
Section titled “Canonical attribute names”For the channels you’ll reach for most often, use the typed constants instead of bare strings. The loader, the shader, and any later glTF import all agree on names without bikeshedding:
Position is set via Vertex::new(...) and is not part of the constants table; the rest live as &'static str literals on Vertex:
| Constant | Value | Notes |
|---|---|---|
Vertex::NORMAL |
"normal" |
Per-vertex normal. |
Vertex::TANGENT |
"tangent" |
Per-vertex tangent (for normal-mapped shading). |
Vertex::UV0 / UV1 |
"uv0" / "uv1" |
Texture coordinates. Use the numbered form when a mesh carries multiple UV sets (typical for glTF). |
Vertex::COLOR0 / COLOR1 |
"color0" / "color1" |
Vertex colours. |
The constants are plain &'static str literals: vertex.set(Vertex::UV0, [...]) and vertex.set("uv0", [...]) do the same thing. Mix the two styles freely; the constants exist to prevent typos and to give the glTF loader a stable vocabulary.
Example
Section titled “Example”use fragmentcolor::mesh::Vertex;let v = Vertex::new([0.0, 0.0, 0.0]) .set(Vertex::UV0, [0.5, 0.5]) .set(Vertex::NORMAL, [0.0, 1.0, 0.0]);import { Vertex } from "fragmentcolor";const v = new Vertex([0.0, 0.0, 0.0]).set("uv0", [0.5, 0.5]).set("normal", [0.0, 1.0, 0.0]);from fragmentcolor import Vertexv = Vertex([0.0, 0.0, 0.0]).set(Vertex.UV0, [0.5, 0.5]).set(Vertex.NORMAL, [0.0, 1.0, 0.0])import FragmentColorlet v = try Vertex([0.0, 0.0, 0.0]).set("uv0", [0.5, 0.5]).set("normal", [0.0, 1.0, 0.0])import org.fragmentcolor.*val v = Vertex(listOf(0.0f, 0.0f, 0.0f)).set("uv0", floatArrayOf(0.5f, 0.5f)).set("normal", listOf(0.0f, 1.0f, 0.0f))Methods
Section titled “Methods”Vertex::new
Section titled “Vertex::new”Construct a Vertex from a 2D or 3D position. Set additional attributes
(uv, color, custom keys) with set to match the per-vertex inputs
your shader declares.
Example
Section titled “Example”use fragmentcolor::mesh::Vertex;let v = Vertex::new([0.0, 0.0]);import { Vertex } from "fragmentcolor";const v = new Vertex([0.0, 0.0]);from fragmentcolor import Vertexv = Vertex([0.0, 0.0])import FragmentColorlet v = try Vertex([0.0, 0.0])import org.fragmentcolor.*val v = Vertex(listOf(0.0f, 0.0f))Vertex::pbr
Section titled “Vertex::pbr”Construct a Vertex that matches the PBR shader’s vertex input layout.
Seeds every attribute Material::pbr
reads (NORMAL, UV0, COLOR0, UV1, TANGENT) with a neutral identity
default; chain .set(...) to override the slots a real mesh has data for.
A vertex built with Vertex::pbr(pos) alone renders the same way
Scene::load renders a
glTF primitive that carries only POSITION. Face-normal computation, UV
defaults, vertex tint, and tangents all collapse to the values the loader
falls back to. Useful for building PBR meshes by hand without spelling
out every default attribute.
Defaults:
| attribute | default value | what it means |
|---|---|---|
NORMAL |
[0, 0, 1] |
forward-facing surface |
UV0 |
[0, 0] |
corner of the texture |
COLOR0 |
[1, 1, 1, 1] |
identity vertex tint |
UV1 |
[0, 0] |
unused; only matters for maps that opt into UV1 |
TANGENT |
[1, 0, 0, 1] |
T = +X, bitangent sign +1 |
Example
Section titled “Example”1 collapsed line
fn main() -> Result<(), Box<dyn std::error::Error>> {use fragmentcolor::{Mesh, Vertex};
// Build a triangle; override only what the mesh actually carries — NORMAL// / COLOR0 / UV1 / TANGENT use their identity defaults from Vertex::pbr.let mesh = Mesh::new();mesh.add_vertex(Vertex::pbr([ 0.0, 0.5, 0.0]).set(Vertex::UV0, [0.5, 1.0]));mesh.add_vertex(Vertex::pbr([-0.5, -0.5, 0.0]).set(Vertex::UV0, [0.0, 0.0]));mesh.add_vertex(Vertex::pbr([ 0.5, -0.5, 0.0]).set(Vertex::UV0, [1.0, 0.0]));2 collapsed lines
Ok(())}import { Mesh, Vertex } from "fragmentcolor";
// Build a triangle; override only what the mesh actually carries — NORMAL// / COLOR0 / UV1 / TANGENT use their identity defaults from Vertex.pbr.const mesh = new Mesh();mesh.addVertex(Vertex.pbr([ 0.0, 0.5, 0.0]).set("uv0", [0.5, 1.0]));mesh.addVertex(Vertex.pbr([-0.5, -0.5, 0.0]).set("uv0", [0.0, 0.0]));mesh.addVertex(Vertex.pbr([ 0.5, -0.5, 0.0]).set("uv0", [1.0, 0.0]));from fragmentcolor import Mesh, Vertex
# Build a triangle; override only what the mesh actually carries — NORMAL# / COLOR0 / UV1 / TANGENT use their identity defaults from Vertex.pbr.mesh = Mesh()mesh.add_vertex(Vertex.pbr([ 0.0, 0.5, 0.0]).set(Vertex.UV0, [0.5, 1.0]))mesh.add_vertex(Vertex.pbr([-0.5, -0.5, 0.0]).set(Vertex.UV0, [0.0, 0.0]))mesh.add_vertex(Vertex.pbr([ 0.5, -0.5, 0.0]).set(Vertex.UV0, [1.0, 0.0]))import FragmentColor
// Build a triangle; override only what the mesh actually carries — NORMAL// / COLOR0 / UV1 / TANGENT use their identity defaults from Vertex.pbr.let mesh = Mesh()try mesh.addVertex(Vertex.pbr([ 0.0, 0.5, 0.0]).set("uv0", [0.5, 1.0]))try mesh.addVertex(Vertex.pbr([-0.5, -0.5, 0.0]).set("uv0", [0.0, 0.0]))try mesh.addVertex(Vertex.pbr([ 0.5, -0.5, 0.0]).set("uv0", [1.0, 0.0]))import org.fragmentcolor.*
// Build a triangle; override only what the mesh actually carries — NORMAL// / COLOR0 / UV1 / TANGENT use their identity defaults from Vertex.pbr.val mesh = Mesh()mesh.addVertex(Vertex.pbr(listOf(0.0f, 0.5f, 0.0f)).set("uv0", floatArrayOf(0.5f, 1.0f)))mesh.addVertex(Vertex.pbr(listOf(-0.5f, -0.5f, 0.0f)).set("uv0", floatArrayOf(0.0f, 0.0f)))mesh.addVertex(Vertex.pbr(listOf(0.5f, -0.5f, 0.0f)).set("uv0", floatArrayOf(1.0f, 0.0f)))Vertex::set
Section titled “Vertex::set”Attach an arbitrary property to the vertex.
Locations and mapping:
- When you call
set(key, value)for the first time for a given key, the Vertex assigns the next available@location(N)to that property (starting after position). Subsequent calls reuse the same location. - At render time, shader vertex inputs (declared with
@location(N)) are derived from the Shader and matched to Vertex/Instance properties by:- explicit location (instance first, then vertex), then
- name (instance first, then vertex).
- There is no special-case for location(0) in the mapping; position is just another vertex attribute exposed as
positionwith a 2- or 3-component format depending on how you constructed the Vertex.
Planned explicit control:
- You will be able to pin a property to a specific location using a fluent API:
vertex.set(key, value).at(index). - Vertex construction may also support
Vertex::from_shader(&Shader)to derive an initial layout directly from the shader AST.
Example
Section titled “Example”use fragmentcolor::mesh::{Vertex, VertexValue};let v = Vertex::new([0.0, 0.0, 0.0]).set("weight", 1.0).set("color",[1.0, 0.0, 0.0]);import { Vertex } from "fragmentcolor";const v = new Vertex([0.0, 0.0, 0.0]).set("weight", 1.0).set("color",[1.0, 0.0, 0.0]);from fragmentcolor import Vertexv = Vertex([0.0, 0.0, 0.0]).set("weight", 1.0).set("color",[1.0, 0.0, 0.0])import FragmentColorlet v = try Vertex([0.0, 0.0, 0.0]).set("weight", 1.0).set("color",[1.0, 0.0, 0.0])import org.fragmentcolor.*val v = Vertex(listOf(0.0f, 0.0f, 0.0f)).set("weight", 1.0).set("color", listOf(1.0f, 0.0f, 0.0f))Vertex::create_instance
Section titled “Vertex::create_instance”Create an Instance that inherits this vertex’s attributes. Use this
when you want a starting point for an instance whose per-instance values
mostly match the source vertex, then call set on the result to change
the fields that differ.
Example
Section titled “Example”use fragmentcolor::mesh::Vertex;let v = Vertex::new([0.0, 0.0]);let inst = v.create_instance();1 collapsed line
_ = inst;import { Vertex } from "fragmentcolor";const v = new Vertex([0.0, 0.0]);const inst = v.createInstance();from fragmentcolor import Vertexv = Vertex([0.0, 0.0])inst = v.create_instance()import FragmentColorlet v = try Vertex([0.0, 0.0])let inst = v.createInstance()import org.fragmentcolor.*val v = Vertex(listOf(0.0f, 0.0f))val inst = v.createInstance()