Builtin Library
- ..
- .metadata267 B
- README.md8.0 KB
- init.luau14.1 KB
- to_points.module
README.md
instances.to_points (procNode)
Place a piece set on target points, as Copy to Points does: one copy of a piece at each target point, standing where the point's instance attributes put it, followed by the piece point's own pose. With match off every target point gets every piece; with match on a piece goes only to the target points whose attribute value it shares. The copies are a population: io.output draws them as instanced draws, one per piece and material, io.export saves them as a population asset, and instances.unpack takes them apart. Use it to dress a building's window slots with window pieces, scatter rocks of several kinds, or stamp any kit of meshes onto points.
The pieces
pieces is a piece set: points that each carry a Mesh record ({ mesh = <geometry> }), made by instances.pack (one geometry, or one piece per value of an attribute) or by geometry.merge (several geometries with pack on, or the piece sets of several instances.pack calls merged). A piece point's place in the set is its index, counted from 0. Points carrying no Mesh record are refused.
Where a copy stands
Each target point's pose is composed by points.pose from the attributes it carries: P, orient, scale, N, v, up, pscale, rot, trans and pivot (pivot, then scale as scale times pscale, then orientation (orient, else the alignment of N, else of v, with +Z along it and +Y toward up), then rot, then trans plus P). An attribute the target points do not carry at all is identity. A point set holds an attribute for every point or for none: points that lacked an instance attribute when they were merged with points that carried it hold its identity (geometry.merge: scale and pscale 1, orient and rot unrotated, up +Y), so they pose as they did alone. A point whose scale or pscale is 0 gives a copy of no size, which draws nothing.
The piece point's own P, orient and scale (identity where the piece set carries none) are placed inside the target's pose: the copy stands at the target's P plus the piece's P scaled by the target's scale and turned by the target's orientation, turned by the target's orientation followed by the piece's, at the target's scale times the piece's. A piece packed by instances.pack stands at the origin, unturned, at scale 1, so its copy stands at the target's pose.
Matching
With match on, the value of attribute on each target point is compared with the same attribute on each piece, and a pair whose values are equal makes a copy (every equal pair, so two pieces holding one value both go to its target points):
- The attribute holds a number or a text; an attribute of vectors is refused. Numbers compare as numbers and texts as texts, a number never equals a text.
- When the pieces carry no such attribute, a target point's number is the 0-based index of the piece it takes (
instances.packwithgroup = "", or a piece set whose pieces were merged without one). - Target points that carry no such attribute are refused, naming the attribute.
- A target point whose value no piece holds is refused, naming the value and how many points hold it, with
unsupported"refuse"; with "report" it goes without a copy and is listed inreport.
instances.pack writes each piece's value under group (default variant): its name, else the value it was split by, else 0 for a whole geometry. geometry.merge with pack writes the name from names, else the piece's index among the geometries. Pieces packed one by one with no name all carry 0.
With match on, the copies are allocated for every pair of target point and piece and counted on the device: count() answers the slots (target points times pieces) and the live count the matched copies.
What a copy carries
Each copy carries P, orient, scale and id (every copy a point of its own), piece (its piece's place in the set), its Mesh (the piece's record when the set holds one piece; with several, the piece's place, which indexes the records the detail lists in labels.Mesh), and the target point attributes carry selects. Each record labels.Mesh lists states the material its piece wears as material and the most copies of the piece there can be as capacity; each piece's draw reserves that many slots. With match on and the target points' attribute read where the node cooks, capacity is the number of target points holding the piece's value, so the draws of the pieces reserve one slot per matched copy between them, and a piece no target point holds has no draw. Target points whose attribute the device rewrites as time moves (a solver's points, or a column that reads the clock) can hold any piece's value at a later time: each piece then states one per target point. With match off every piece states one per target point. The piece set rides in the detail as pieces, and the detail's population names the attributes a reader takes the piece, the pose and the material from.
A copy wears material when it is wired; else its target point's Material when the target points carry one; else its piece's, the material the piece was packed with. Merged with other populations (geometry.merge), the copies stay one population, drawn as one instanced draw per distinct piece and material.
Inputs
points(Points). The target points. A copy is made at each, at the pose its attributes state.pieces(Points). The piece set: one point per piece, each carrying itsMeshrecord.match(Bool, default false). On, a piece is copied to a target point when theirattributevalues match (every match). Off, every target point gets every piece.attribute(String, default "variant"). The attribute, on both the pieces and the target points, thatmatchcompares: a number or a text. An integer on the target points with no such attribute on the pieces is the 0-based index into the piece set.oriented(Bool, default true). On, copies take the target points' orientation. Off, copies stand unturned at the target's position (scaleandpscaleare still read).carry(String, default "*"). The target point attributes carried onto each copy: a space-separated pattern of names,*matching any run of characters.P,orient,scaleandidalways stand on a copy.material(Points or AssetRef<material>, optional). A material every copy wears, in place of its piece's: what a material node answers, or a reference to a material asset.castsShadows(String, default "", one of "", "true", "false"). Whether the copies cast shadows when drawn: "false" for smoke, sparks and glow cards, "true" for debris and props. Empty keeps what each piece states (instances.packstates it).receivesShadows(String, default "", one of "", "true", "false"). Whether the copies receive shadows when drawn. Empty keeps what each piece states.unsupported(String, default "refuse", one of "refuse", "report"). What happens to a target point that holds a value no piece holds, whenmatchis on. Over points a solver moves the match is made on the device and the check is answered from a read the device delivers a frame after the frame it is about: the refusal fails the play then, and the report row stands in the play's report (reported) rather than inreport.
Outputs
instances(Geometry). The copies, a population.report(List). Withmatchon andunsupported"report", one row{ attribute, count, value }when some target points matched no piece (valueis the first such value); empty otherwise.
local kit = g.geometry.merge({ a = g.mesh.box({}), b = g.mesh.sphere({}), pack = true }).value
local slots = g.points.grid({ countX = 3, countZ = 1, spacing = 2 })
slots.point.variant = g.where(g.compare(g.element.id, "eq", 1), 1, 0)
local placed = g.instances.to_points({ points = slots, pieces = kit, match = true }).instances