@comfyorg/quad-wireframe-three

中文文档
0.1.0发布于 2026/09/22MITTypeScriptES Module构建来源已验证近 30 天下载 27万

Polygon (quad/n-gon) topology for three.js meshes — read face sizes from binary FBX, rebuild quads from triangle soup with Blender's Tris-to-Quads algorithm, and draw wireframes without triangulation diagonals.

原始使用文档
文档目录7 节
  1. Install
  2. Usage
  3. GLB or any triangulated geometry
  4. FBX (exact polygons)
  5. glTF with FB_ngon_encoding
  6. Lower-level pieces
  7. Performance

@comfyorg/quad-wireframe-three

Polygon topology for three.js meshes: draw wireframes that show quads and n-gons instead of triangulation diagonals.

three.js only knows triangles. material.wireframe = true draws every edge of every triangle, so a quad mesh (retopologised characters, Tripo/Meshy "quad" output, anything from Blender) shows up with a diagonal through each face. EdgesGeometry cannot fix it either: on a non-planar quad the diagonal has a dihedral angle just like a real edge. This package restores the polygons and draws only their perimeter.

Two ways in:

SourceMethodExactness
Binary FBXRead the face sizes straight from PolygonVertexIndex, then regroup the triangles three.js FBXLoader emits.Exact (the file has the data).
glTF with FB_ngon_encodingDecode the fan-ordered index buffer the extension prescribes (Nomad Sculpt and assimp write it).Exact (the order is the data).
GLB / OBJ / STL / anythingRebuild quads with a port of Blender's Tris to Quads (bmo_join_triangles), best-first merge on quad quality.~98% of the original edges on a noisy retopo mesh.

The output is a plain PolygonMesh (welded positions + face offsets, the same layout Blender uses) and a BufferGeometry ready for THREE.LineSegments.

Install

npm install @comfyorg/quad-wireframe-three three

Usage

GLB or any triangulated geometry

import { LineBasicMaterial, LineSegments } from 'three'
import { createQuadWireframeGeometry } from '@comfyorg/quad-wireframe-three'

const wire = new LineSegments(
  createQuadWireframeGeometry(mesh.geometry),
  new LineBasicMaterial({ color: 0xffffff })
)
mesh.add(wire)

createQuadWireframeGeometry welds the vertices, runs the Blender Tris-to-Quads merge and returns the polygon edges. Tune or disable the merge:

createQuadWireframeGeometry(geometry, { join: false }) // plain triangle edges
createQuadWireframeGeometry(geometry, {
  join: {
    faceAngleThreshold: (60 * Math.PI) / 180, // Blender "Max Face Angle", default 40°
    shapeAngleThreshold: (60 * Math.PI) / 180, // Blender "Max Shape Angle", default 40°
    topologyInfluence: 2 // Blender "Topology Influence" 0..2, default 1
  }
})

FBX (exact polygons)

import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader.js'
import {
  createQuadWireframeGeometry,
  matchFbxPolygons,
  readFbxPolygons
} from '@comfyorg/quad-wireframe-three'

const buffer = await (await fetch(url)).arrayBuffer()
const root = new FBXLoader().parse(buffer, '')
const polygons = readFbxPolygons(buffer)

root.traverse((child) => {
  if (!(child as Mesh).isMesh) return
  const mesh = child as Mesh
  const faceSizes = matchFbxPolygons(polygons, mesh.geometry)
  const wire = createQuadWireframeGeometry(mesh.geometry, { faceSizes })
  mesh.add(new LineSegments(wire, material))
})

Why a separate reader instead of FBXLoader: the loader does parse PolygonVertexIndex, but genFace earcuts every polygon and only pushes triangle vertices, so the per-face vertex count is never stored anywhere on the output; and its BinaryParser and parsed tree are module-private (FBXLoader.js exports only FBXLoader), so there is no hook to read them either. readFbxPolygons is therefore a small binary FBX reader of its own (zlib arrays via fflate, the same library FBXLoader uses) that stops at Objects/Geometry/PolygonVertexIndex and skips textures, animation and everything else; it does not parse ASCII FBX. matchFbxPolygons pairs each record with a three.js geometry by the name FBXLoader copies onto it, falling back to a unique triangle count, and returns null when it cannot be sure — pass faceSizes: null and the geometric rebuild runs instead.

glTF with FB_ngon_encoding

glTF itself has no polygons, but the draft FB_ngon_encoding extension encodes them in the triangle order: each polygon is a fan anchored on one vertex, and consecutive polygons never share their anchor, so a polygon boundary is wherever triangle.vertex[0] changes. GLTFLoader keeps the index order and records the flag on mesh.userData.gltfExtensions:

import { hasNgonEncoding, ngonEncodedFaceSizes } from '@comfyorg/quad-wireframe-three'

const faceSizes = hasNgonEncoding(mesh) ? ngonEncodedFaceSizes(mesh.geometry) : null
const wire = createQuadWireframeGeometry(mesh.geometry, { faceSizes })

Only decode when the flag is present: on an unflagged mesh, neighbouring triangles that happen to share a first index would be merged by mistake. Anything that reorders indices (Draco, meshopt, most optimisers) destroys the encoding and drops the flag with it.

Lower-level pieces

import {
  polygonMeshFromTriangles, // BufferGeometry (+ optional faceSizes) → PolygonMesh
  joinTriangles, // Blender Tris to Quads on a PolygonMesh
  polygonEdges, // unique [a, b, a, b, ...] vertex pairs
  createPolygonEdgesGeometry, // PolygonMesh → LineSegments geometry
  faceSizesOf, // per-face vertex counts (3 = tri, 4 = quad, ...)
  weldPositions,
  parseBinaryFbx
} from '@comfyorg/quad-wireframe-three'

faceSizesOf is also how you get honest Faces statistics (Blender's "Faces" rather than a triangle count) for a loaded model.

Performance

Welding uses a string-keyed map and the merge is O(E log E); both run on the main thread in plain JS. For meshes in the hundreds of thousands of triangles, run polygonMeshOf in a worker and transfer the resulting typed arrays.

版本与发布记录

@comfyorg/quad-wireframe-three 共发布 1 个版本,首次发布于 2026/09/22;过去 12 个月发布了 1 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.1.0latest2026/09/221 个月前

关于 @comfyorg/quad-wireframe-three 的常见问题

@comfyorg/quad-wireframe-three 是什么?

Polygon (quad/n-gon) topology for three.js meshes — read face sizes from binary FBX, rebuild quads from triangle soup with Blender's Tris-to-Quads algorithm, and draw wireframes without triangulation diagonals.

如何安装 @comfyorg/quad-wireframe-three?

在项目目录运行 npm install @comfyorg/quad-wireframe-three,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:three(>=0.160.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@comfyorg/quad-wireframe-three 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@comfyorg/quad-wireframe-three 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@comfyorg/quad-wireframe-three 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=18。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@comfyorg/quad-wireframe-three 可以商用吗?

@comfyorg/quad-wireframe-three 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@comfyorg/quad-wireframe-three 的下载量有多少?

根据 npm 官方下载统计,@comfyorg/quad-wireframe-three 在 2026/10/11 – 2026/11/09 这 30 天内被下载 269,973 次,日均约 8,999 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@comfyorg/quad-wireframe-three 还在维护吗?

最新版本 0.1.0 发布于 2026/09/22(1 个月前);至今共发布 1 个版本,其中过去 12 个月发布了 1 个;首次发布于 2026/09/22。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。