flutter3d_mesh 0.8.0
flutter3d_mesh: ^0.8.0 copied to clipboard
An editable mesh for flutter3d: half-edge topology on persistent typed arrays, selections, and the operations a modeller runs on them.
0.8.0 #
clusterMesh reorders a mesh's triangles into runs a renderer can cull.
clusterMesh(mesh, maxTriangles: 4096, minTriangles: 1024, foldAngle: pi / 4)
grows each run across shared positions, keeping it near its centre and close
to its average normal, and records every run's box and cone of normals in
MeshData.clusters. No vertex moves and the index buffer holds the same
triangles in a new order, so anything that ignores the table draws the whole
mesh as before. flutter3d_build's --chunks calls it for meshes above 65536
triangles. C9
simplifyMeshWithAttributes takes a targetError. A collapse that would
bend the surface further than that is refused, so the run can stop short of
targetTriangleCount. Left out, the result is what 0.7 returned.
simplifyMeshWithAttributesMeasured is the same pass and returns a
SimplifiedMesh: the mesh and its error, the largest error any accepted
collapse reached, in the mesh's own units and measured against the original
faces alone, without the boundary penalty. It is a loose upper bound (on a
sphere cut to a tenth it reads about six times the furthest any vertex moved),
good for comparing levels and for a budget. The pass is deterministic, so the
error only grows as the target falls, and a chain of levels cut from one base
has errors in the same order as its ratios. C5
Its flutter3d_* dependencies ask for ^0.8.0.
0.7.1 #
Released with the rest of the stack at 0.7.1. Nothing in this package changed. The release it resolves against builds from pub.dev again and no longer crashes Metal on the first unlit draw.
Its flutter3d_* dependencies ask for ^0.7.1, and it asks for vector_math ^2.4.3.
0.7.0 #
The first publication. The 0.6.0 below calls itself the first release, and
it was the first inside the workspace: it never reached pub.dev. 0.7.0 is the
number the whole shelf goes out on, so that one number names one tree and
^0.7.0 on any flutter3d_* package resolves against every other;
doc/boundary-0.7.0.md lists the thirteen packages that begin here. What
0.6.0 describes was a spike of three entries. The rest of the package is below.
-
Breaking against the spike.
EditMesh.halfEdgesOf(face)is gone; walk a face withforEachHalfEdge.verticesOfreturns aList<int>where it returned anIterable<int>.vertexCountandfaceCountcount live elements, and an id is a slot that may hold a deleted one.toMeshDatawas a flat-normal fan and now goes throughMeshLayoutPlan, so the vertex count of the same mesh differs.MeshData,RayandTriangleBvhcome frompackage:flutter3d_core/geometry.dart;flutter3d_geometrywas folded intoflutter3d_core, which is the one sibling this package depends on. -
EditMeshkeeps a journal, and deletion is a tombstone. Storage isJournalledFloatsandJournalledInts, andbeginStep/endStepbracket aJournalStepthat covers all nine arrays or none, so positions and topology cannot be undone to different versions.undoandredowalk it,abandonStepdrops a step without clearing redo, andjournalByteswithdropOldestJournalStepsserve a history budget counted in bytes. A journal of previous values measured 2% of a full copy where chunked copy-on-write measured 92 to 100% on scattered edits.deleteFaceanddeleteVertexleave slots behind so ids stay stable, andcompact()returns the packed mesh with anIdRemap. The mesh grows in place throughaddVertex,addFace,splitEdge,splitFace,weldTwinsandcutTwin. -
Attributes live in four domains and cost nothing until written.
MeshAttributenamesuv0,colour,weights,joints,crease,flagsandmaterialSlot. UVs are per corner, because a seam is two faces disagreeing about one vertex;EdgeFlags.sharpandEdgeFlags.seamare written to both halves of an edge;FaceFlags.smoothis per face. A layer nobody wrote answers a neutral value and is not allocated, and one created late is padded back to the journal's depth. -
Conversion to and from
MeshDatais three decisions made in the open.importMeshDatawelds by position withinmax(diagonal * 1e-6, 1e-9), rebuilds polygons and returns the mesh, anImportReportand the file's morph targets asShapeKeys. Going back,MeshLayoutPlan.buildear-clips concave faces, splits normals atMeshNormals.defaultSmoothAngleof 30 degrees, merges identical corners into one GPU vertex and keepsgpuVertexToVertex,gpuVertexToCornerandtriangleToFace. A kept plan refills positions without rebuilding: at 200 thousand triangles the full conversion measured 102 ms, a refill 6.7 ms and a refill of 40 vertices 1.5 µs. Import was quadratic, 6.6 s at 50 thousand triangles, and is 101 ms after the edge-key fix.toBytesandfromBytesare a little-endian tagged format that skips sections it does not know, andeditInIsolateruns aMeshWorkon another isolate, or inline on the web. -
Normals.
MeshNormals.buildweights by corner angle, without which a cube corner moves by 19 degrees depending on how the cube was triangulated, and breaks averaging at a face that is not smooth, a sharp edge or an angle past the threshold.rebuildAroundrecomputes only the fans around moved vertices.flipNormalsandmakeConsistentare onEditMesh;makeConsistentleaves an open island as it found it. -
Selectionis a value outside the journal, so undo does not undo a click. It has anElementLevelof vertex, edge or face, an edge id is the smaller of its two half-edges, and it offersunion,difference,intersection,toggle,convertedTo,grown,shrunk,linked,boundary,Selection.edgeLoop,Selection.edgeRingandSelection.byMaterialSlot. A loop steps only through valence 4 and a ring only across quads. -
Operations take
(mesh, selection, params)and return anOpResult. A refusal isOpResult.refusedwith a sentence.translateSelection,rotateSelection,scaleSelectionandtransformSelectionpivot onmedianOf;extrudeFacesbuilds no interior walls for a region;loopCut,insetFaces,bevelEdges,bevelVertices,bridgeLoops,slideEdges,triangulateFaces,deleteSelection,duplicateSelectionandsplitSelectionare the rest of that shape. Others are shaped differently:mergeByDistanceandmergeAtrebuild the mesh and return it with aMergeReport,separateComponentsreturns a mesh and anIdRemapper island, anddissolveEdgeanddissolveVertexare methods onEditMesh. Extruding one face of a box gives 12 vertices, 20 edges and 10 faces with the Euler characteristic still 2. Not built: a bevel with more than one segment, which is refused in a sentence, a knife, andinsetFaces(individual:), which is accepted and has no effect. -
Checks, holes and picking.
MeshChecks.allreports n-gons, boundary edges, non-manifold vertices, isolated vertices, degenerate faces, duplicate vertices and inverted shells asMeshIssues that each carry aselection.fillHoles(mesh)closes every boundary chain and returns how many it closed.MeshBvhanswersraycast,facesInAabbandfacesInFrustumand canrefitwithout a rebuild: at 200 thousand triangles a rebuild measured 55 ms, a refit 5 ms and one ray 0.5 µs, and 10 000 random rays agree with a brute-force scan.MeshPickerfindsfaceAt,vertexNearandedgeNear. -
Parametric shapes, recipes and fixtures.
ParametricShapeis sealed overParametricCuboid,ParametricPlane,ParametricLathe,ParametricSphere,ParametricCylinderandParametricTorus; each builds quads with sharp rings where the engine's primitives duplicate vertices, a flat cap is one n-gon, and the UVs match the engine's corner for corner.recipes()lists sixteen builders in fourRecipeCategorys, fromfloorLamptowindow, at real dimensions.package:flutter3d_mesh/testing.dartis a second library with eight fixture meshes. -
Modifiers.
Modifieris sealed overArrayModifier,MirrorModifier,SmoothModifier,SubdivisionModifierandBooleanModifier, each withtoJson;modifierFromJsonanswers null for a kind it does not know.ModifierStack.evaluateis memoized on the identity of the base and of the operands, since an operand can change while the base does not.catmullClarkhonours semi-sharp creases and interpolates UVs per face so seams stay, and ten passes ofsmoothVerticeswithpreserveVolumeshrink a sphere's mean radius by 0.04% where plain Laplacian smoothing shrinks it by 26%.mirror(bisect: true)throwsUnimplementedError, and the array and mirror modifiers carry positions, topology and material slots and no UVs, colours, creases or skin. -
Booleans.
booleanUnion,booleanSubtractandbooleanIntersectreturn aCsgResultor null pastmaxPolygons, 200 000 by default, checked before the work and again on the result. The BSP runs on its own stack so a deep tree does not overflow the native one. Coplanar polygons are counted intoCsgResult.warnings. The result can contain T-junctions, so it may not be watertight; its volume is right. -
UV.
projectUvwithUvProjection.planaror.box,splitIslandsalong seams,lscmsolved by conjugate gradients with a Jacobi preconditioner,unwrapMeshwithonProgressandisCancelled,stretchOfreading 1.0 for an isometry, andpackIslands, a shelf packer that may rotate by 90 degrees. -
Skin weights and shape keys.
paintWeight,normalizeVertexWeights,pruneVertexWeights,mirrorWeights,smoothVertexWeightsandgradientWeightsedit theweightsandjointslayers, andlimitInfluencesholds them to a count. AShapeKeyis a set of positions thatgrownToandremappedBykeep in step with the mesh, andshapeKeyMorphTargetsemits one delta per GPU row of aMeshLayoutPlan. -
Simplification, remeshing and collision shapes.
simplifyMeshandsimplifyMeshWithAttributestake and returnMeshData, the second holding borders withboundaryWeight.isotropicRemeshandretopologizereturn anEditMesh; the remesher assumes a closed mesh and does not project back onto its source.computeConvexHull,decomposeConvexwith a default of 12 pieces,fitBoxByInertia,fitSphereByInertia,fitCapsuleByInertiaandstaticMeshShapemake what a physics world needs. There is no LOD chain in this package;LodSpecisflutter3d_model_core's. -
Sculpting, multiresolution and baking.
SculptMeshstores a mesh in chunks of 1024 vertices withdirtyChunks, so a stroke re-uploads what it touched.applyBrushStroketakes aBrushof eightBrushKinds and threeBrushFalloffs, withsymmetryX.Multireskeeps levels withmeshAt,displaceanddisplacementMap; a million vertices over four levels measured 4.1 s.bakeNormalMap,bakeAmbientOcclusion,bakeCurvatureandbakeThicknessfill aBakedMapthroughrasterizeUv, anddilatepads the islands.projectBrushmaps a 3D brush toUvSpans for texture paint. Dynamic topology is not built. -
Plain Dart. Dependencies are
flutter3d_core^0.7.0andvector_math. The archive carriesskills/flutter3d-mesh-editing-topology/for a coding agent, installed withdart run skills@ get.
0.6.0 #
The first release, taking the set's number rather than a first of its own, because the whole workspace goes out together.
-
EditMesh: half-edge topology overInt32ListandFloat32List. Faces of any valency, twins matched by the pair of vertices an edge runs between, boundaries answered as boundaries rather than as a face that is not there. -
A cuboid as six quads, an extrusion, and the way back to
MeshData. Quads and not triangles, which is the point: a loop cut through a triangulated cube has nothing to cut along. -
validate()and the Euler characteristic, because every invariant here has been broken silently by an operation in some modeller — anextthat leaves its face, a twin that is not mutual — and each is invisible until a loop walk runs forever.