A flexible, memory compact, fast and dynamic CSG implementation on top of three-mesh-bvh

Overview

three-bvh-csg

lgtm code quality build github twitter sponsors

An experimental, in progress, flexible, memory compact, fast and dynamic CSG implementation on top of three-mesh-bvh. More than 100 times faster than other BSP-based three.js CSG libraries in complex cases.

Contributions welcome!

Roadmap

  • Fix triangle splitting / missing triangle issues
  • Hierarchical operations #6
  • Polygon splitting #4
  • Worker Support #14

Examples

Simple CSG

Complex Model CSG

Multimaterial CSG

Use

import { SUBTRACTION, Brush, Evaluator } from 'three-bvh-csg';
import { MeshStandardMaterial, Mesh, SphereGeometry, BoxGeometry } from 'three';

const csgEvaluator = new Evaluator();
const brush1 = new Brush( new SphereGeometry() );
const brush2 = new Brush( new BoxGeometry() );

const result = csgEvaluator.evaluate( brush1, brush2, SUBTRACTION );

// render the result!

API

Constants

Operations

CSG operations enums for use with Evaluator.

ADDITION
SUBTRACTION
DIFFERENCE
INTERSECTION

Brush

extends THREE.Mesh

An object with the same interface as THREE.Mesh but used to evaluate CSG operations. Once a brush is created the geometry should not be modified.

Note

It is recommended to remove groups from a geometry before creating a brush if multi-material support is not required.

Evaluator

.useGroups

useGroups = true : Boolean

Whether to use geometry groups when processing the geometry. If geometry groups are used then a material array and groups will be assigned to the target Brush after processing. If groups are disabled then a single coherent piece of geometry with no groups will be produced.

.evaluate

evaluate(
	brushA : Brush,
	brushB : Brush,
	operation : Operation,
	target = null : Brush | Mesh
) : Brush | Mesh

Performs the given operation on brushA with brushB. If no target is provided then a new Brush will be created with the new geometry. Otherwise the provided Brush will be modified in place and geometry disposed or marked for update as needed.

Gotchas

  • All geometry are expected to have all attributes being used and of the same type.
  • Geometry on a Brush should be unique and not be modified after being set.

References

Comments
  • Add hierarchical operations

    Add hierarchical operations

    Fix #6

    TODO

    • [x] Fill out "evluateHierarchy"
    • [x] Add issue for optimizing evaluation #60
    • [x] Add support for pass through groups
    • [x] Consider providing an option to disable groups #62
    • [x] Add support for a target mesh
    • [ ] Throw an error if Brush is a child
    • [ ] Add "intermediate geometry state" CSG Helper
    • [ ] Add examples
      • ~Add draggable windows, door~
      • Drag snapping
      • Toggleable brush visualiation
    • [ ] Document triangle issues
    • [ ] Harden hierarchy processing
    • [ ] Add a more clear "root" class? RootOperation? OperationCombiner?
    opened by gkjohnson 8
  • Documentation: Update docs to include note about manifold meshes.

    Documentation: Update docs to include note about manifold meshes.

    Hi, Using BufferGeometryUtils.MergeGeometry() on intersected geometries and using that merged geometry with csg operations results with buged faces and missing triangles.

    I created this sandbox to demonstrade: https://codesandbox.io/s/throbbing-breeze-f4hj9p?file=/src/App.js In this sandbox you can see that results don't have faces along x axis.

    documentation question 
    opened by Ceyhun-Wipelot 6
  • Library build fails due to errors in index.d.ts

    Library build fails due to errors in index.d.ts

    Hi everyone! I'm using the three-mesh-csg inside of the library, that is written in Angular 10. With the recent addition of typescript types it stopped building due to errors in the index.d.ts file of three-bvh-csg. There are two main issues:

    1. Generic type 'Array<T>' requires 1 type argument(s). ts(2314) - The "Array" keyword, which is treated as a generic type and requires at least one type argument. I haven't delved deep into the implementation of each particular method, but unless it is specified differently, the fix would be to either add type to the array generic, at least for now, so that typescript will be okay with that, or to add the target type, that the method related to this issue should return.
    export class TriangleIntersectionSets {
    
      addTriangleIntersection( ia: number, tribA: Triangle, ib: number, triB: Triangle ): void;
      getTrianglesAsArray( id?: number ): Array;
      getTriangleIndices(): Array;
      getIntersectionIndices( id: number );
      getIntersectionsAsArray( id?: number, id2: number ): Array;
    
    }
    
    1. A required parameter cannot follow an optional parameter. ts(1016) For the declaration of getIntersectionsAsArray( id?: number, id2: number ): Array the error is concerning the id2 argument, which can't be required. Looking at the implementation of this method, this argument could be also marked as optional.
    bug 
    opened by RobertS21 6
  • Is this library fixed THREE-CSG when dealing with LatheGeometry/ConeGeometry return incorrect result issue?

    Is this library fixed THREE-CSG when dealing with LatheGeometry/ConeGeometry return incorrect result issue?

    I see benchmark/lib include THREE-CSGMesh This issue is added before in intersecting a box with a cone results in a slice of the surface of the cone instead of a solid object I did not test cone , only test LatheGeometry.

    CSG.toMesh(
        CSG.fromMesh(A).intersect(CSG.fromMesh(B)),
        A.matrix,
        A.material
     );
    

    giladdarshan suggest change Line #73 to

    return CSG.fromPolygons(polys.filter(p=>!isNaN(p.plane.normal.x)));
    

    This issue still exists , subtract and union both return partial of the LatheGeometry , intersect returns something close to A-A∩B but not exactly A-A∩B Is this library fixed THREE-CSG when dealing with LatheGeometry/ConeGeometry return incorrect result issue?

    question 
    opened by AsDeadAsADodo 4
  • Extra split triangles no correctly culled

    Extra split triangles no correctly culled

    Follow on from #28

    Looks like there's a triangle not getting split:

    image

    Next step: find the triangle that that's clipped from and the other triangles it's intersecting

    bug 
    opened by gkjohnson 4
  • Missing triangles with torus case

    Missing triangles with torus case

    image

    Possibly a clipping issue. Set both brushes to "torus" and lowest complexity.

    	brush2.position.set( - 0.27300968690619787, 0.5329319712626078, 0 );
    	brush2.scale.setScalar( 1 );
    
    bug 
    opened by gkjohnson 4
  • Memory climbs rapidly during CSG updates in example

    Memory climbs rapidly during CSG updates in example

    image

    Possible sources:

    • *debugger info (should possibly allow for disabling this in the demo?)
    • raycast hit object creation (could be fixed by #3)
    • list of intersection ids
    performance 
    opened by gkjohnson 3
  • Hierarchical operations

    Hierarchical operations

    • Track the last-known calculation state / position associated with each sub node
    • When recomputing traverse the hierarchy (or track ahead of time?) to find which nodes are dirty and bubble it up the hierarchy.
    • Also find which nodes are touching to determine what needs to be recomputed?
    • Recompute the dirty nodes from the bottom up.
    • Unchanged / non-intersecting geometry can just be propagated up.
    • Should operations be declared on the children (ie perform this operation to the parent) or on the parent to perform on the children? I'm leaning towards the former.

    See how Godot handles this? https://docs.godotengine.org/en/stable/tutorials/3d/csg_tools.html

    And RealtimeCSG

    • https://realtimecsg.com/manual.html
    enhancement 
    opened by gkjohnson 3
  • WIP: Update README.md

    WIP: Update README.md

    Related issue: #72

    I tried to add some documentation, I hope it's not a mess. It's not easy. The two classes PointsHelper and HalfEdgeHelper are not documented, I'm not sure about how to explain them.

    Feel free to cancel the PR if the documentation is not consistent. I understand 😅

    opened by AngyDev 2
  • Add typescript

    Add typescript

    Related issue: #65

    • I added the typescript configuration and the typescript lint checks in the package.json script.
    • I added the index.d.ts file, but I'm not sure about some points.

    For example, the Constants type, the HalfEdgeHelper class, I added the code but it is commented because I'm not sure which is the correct type of the halfEdge parameter.

    Maybe it's all a mess 😅, please tell me what you think.

    opened by AngyDev 2
  • Property 'customProgramCacheKey' in type 'GridMaterial' is not assignable to the same property in base type 'MeshPhongMaterial'

    Property 'customProgramCacheKey' in type 'GridMaterial' is not assignable to the same property in base type 'MeshPhongMaterial'

    Property 'customProgramCacheKey' in type 'GridMaterial' is not assignable to the same property in base type 'MeshPhongMaterial'.
      Type '() => boolean' is not assignable to type '() => string'.
        Type 'boolean' is not assignable to type 'string'
    
    opened by kirevdokimov 2
  • Add utility for simplifying flat mesh surfaces

    Add utility for simplifying flat mesh surfaces

    During the process of applying CSG to a mesh flat surfaces become unnecessarily complex. During lulls in user operation it would be good to remesh brushes so they use a minimal number of triangles while still being connected - ie retriangulate flat / coplanar surfaces.

    • Find all large, flat surfaces while retaining half edges
    • Mark shared vertices as being removeable or not - ie required to define the contour of a shape.
    • Remove unnecessary vertices from the new shapes
    • Earcut to triangulate the surfaces
    performance 
    opened by gkjohnson 1
  • Example that reproduces problems with missing triangles (for subtraction), and a possible fix

    Example that reproduces problems with missing triangles (for subtraction), and a possible fix

    I made a demo that allows to interactively "cut off" pieces of a mesh: https://github.com/coolvision/three-bvh-csg/blob/iterative-dev/examples/iterative.js

    demo video: video

    It does subtraction, and the mesh is iteratively updated to the CSG result each time, so that subtractions accumulate. In this case, lots of coplanar triangles are created, and when testing with current three-bvh-csg version, lots of triangles are missing: Screenshot from 2022-12-03 14-29-09

    I've added a fix for adding coplanar triangles, and in this case it works fine: https://github.com/gkjohnson/three-bvh-csg/commit/718868914c96f11b28bf5b194eca0c9953d0af0f#diff-172f17787715fc8aa7225b6a6716874d91c005133ed9670a86c84ab2081ca063 Screenshot from 2022-12-03 14-42-16

    When I use a cone for the brush instead of a box. there are still missing and non-clipped triangles, albeit less than without the fix, so there is still work to do: Screenshot from 2022-12-03 14-29-58

    Would some of this be useful? I can add a PR for the fix, and for the demo/example as well, I think it's a useful testcase for improving bunch of corner cases.

    opened by coolvision 4
  • buildFunctions line 770 countNodes sometimes has undefined property

    buildFunctions line 770 countNodes sometimes has undefined property

    This is the error stack. Same two Brush es , sometimes it's ok , sometimes got this error.

    buildFunctions.js:785 Uncaught TypeError: Cannot read properties of undefined (reading 'count')
        at countNodes (buildFunctions.js:785:13)
        at countNodes (buildFunctions.js:791:15)
        at buildPackedTree (buildFunctions.js:770:19)
        at new MeshBVH (MeshBVH.js:165:18)
        at Brush.prepareGeometry (Brush.js:68:26)
        at Evaluator.evaluate (Evaluator.js:137:5)
        at csg (animationTest.js:767:39)
        at animationTest.js:778:30
    
    opened by AsDeadAsADodo 7
  • Cache results objects for complex shapes?

    Cache results objects for complex shapes?

    Right now the Operations groups/hierarchy etc have to be completely rerun for for the entire stack each time it’s evaluated.

    so really big objects end up having to include all previous brushes/mesh data and be processed each time.

    one idea:

    let’s say there are 10 operation steps, and you are transforming step #7. You could run a truncated steps 1-6, and save the result. Then use that result each subsequent pass. I think if all something like addition you could also run steps 8-10 and generate a brush for that section as well. This would make a transformation of any size just 3 brushes instead of 10.

    plus the results discard unused geometry data so hypothetically run much faster.

    Another idea would be to cache by hierarchy.

    so for a body for example, a arm/leg etc would cache the results.

    This might not be something for the library to do directly persay, But would be a good example on how to use it in production

    I’m going to try to make some tests

    performance 
    opened by DennisSmolek 1
  • ExtrudeGeometry subtract BoxGeometry obtain part of BoxGeometry's faces and material

    ExtrudeGeometry subtract BoxGeometry obtain part of BoxGeometry's faces and material

    I'll just skip the set up part ,directly show the pics and codes. 图片 图片

    After csg substract , the result also obtain some faces and material of the box.

    Console print Trianlges are coplanar which does not support an output edge

    function extrudeGeometry() {
    	const x = 3;
    	const y = 2;
    	const z = 20 / 100;
    	const wallExtrudeSettings = {
    		depth: y,
    		steps: 2,
    		bevelEnabled: false,
    	};
    	const aroundWallShape = new THREE.Shape();
    	aroundWallShape.moveTo(-x, 0);
    	aroundWallShape.lineTo(-x / 2, (x * Math.sqrt(3)) / 2);
    	aroundWallShape.lineTo(x / 2, (x * Math.sqrt(3)) / 2);
    	aroundWallShape.lineTo(x, 0);
    	aroundWallShape.lineTo(x / 2, (-x * Math.sqrt(3)) / 2);
    	aroundWallShape.lineTo(-x / 2, (-x * Math.sqrt(3)) / 2);
    	aroundWallShape.lineTo(-x, 0);
    	const aroundWallhole = new THREE.Path();
    	aroundWallhole.moveTo(-(x - (z * 2) / Math.sqrt(3)), 0);
    	aroundWallhole.lineTo(
    		-(x - (z * 2) / Math.sqrt(3)) / 2,
    		((x - (z * 2) / Math.sqrt(3)) * Math.sqrt(3)) / 2
    	);
    	aroundWallhole.lineTo(
    		(x - (z * 2) / Math.sqrt(3)) / 2,
    		((x - (z * 2) / Math.sqrt(3)) * Math.sqrt(3)) / 2
    	);
    	aroundWallhole.lineTo(x - (z * 2) / Math.sqrt(3), 0);
    	aroundWallhole.lineTo(
    		(x - (z * 2) / Math.sqrt(3)) / 2,
    		(-(x - (z * 2) / Math.sqrt(3)) * Math.sqrt(3)) / 2
    	);
    	aroundWallhole.lineTo(
    		-(x - (z * 2) / Math.sqrt(3)) / 2,
    		(-(x - (z * 2) / Math.sqrt(3)) * Math.sqrt(3)) / 2
    	);
    	aroundWallhole.lineTo(-(x - (z * 2) / Math.sqrt(3)), 0);
    	aroundWallShape.holes.push(aroundWallhole);
    	const aroundWallExtrudeGeometry = new THREE.ExtrudeGeometry(
    		aroundWallShape,
    		wallExtrudeSettings
    	);
    	aroundWallExtrudeGeometry.rotateX(Math.PI * 0.5)
    	return aroundWallExtrudeGeometry
    }
    const material = new THREE.MeshLambertMaterial({
    	color: 0x00ff00,
    	side: THREE.DoubleSide
    });
    const brush1 = new Brush(extrudeGeometry(), material);
    const scale = 1
    const boxGeometry = new THREE.BoxGeometry(scale, scale, scale)
    boxGeometry.translate(0, -1, 2.5)
    const brush2 = new Brush(boxGeometry, new THREE.MeshLambertMaterial({
    	color: 0x336666
    }));
    const csgEvaluator = new Evaluator();
    // x+ afterCSG
    const result = csgEvaluator.evaluate(brush1, brush2, SUBTRACTION);
    result.position.x = 5
    scene.add(result)
    // x- meshes before CSG 
    scene.add(brush1)
    brush1.position.x = -5
    scene.add(brush2)
    brush2.position.x = -5
    
    bug 
    opened by AsDeadAsADodo 0
Releases(v0.0.2)
Owner
Garrett Johnson
Working on graphics for space robots, path tracing, and having a good time.
Garrett Johnson
With monitors getting wider and websites more compact, maximizing the browser is a waste of space.

With monitors getting wider and websites more compact, maximizing the browser is a waste of space. Widefox utilizes all available vertical real estate while adding extra features to your browsing experience!

null 46 Dec 18, 2022
Text Engraving & Extrusion demo based on Three.js is implemented with Typescript and webpack5. Used THREE-CSGMesh as the core tech to achieve engraving and extrusion results

Text Engraving & Extrusion Text Engraving & Extrusion demo is implemented using Three.js, with Typescript and webpack5. Used THREE-CSGMesh as the core

Jiahong Li 3 Oct 12, 2022
Compact library for interacting with Ankr Scan Multichain API.

ankrscan.js Compact SDK for interacting with Ankr Scan MultiChain JSON-RPC API. SDK supports following MultiChain methods: getLogs - logs matching the

Ankr 23 Jan 3, 2023
🎨 Generate CSS Mesh Gradients

?? Meshgrad Meshgrad is a tiny utility to generate native-CSS Mesh Gradients. Demo - meshgrad.cretu.dev Install $ npm install meshgrad Use Vanilla Jav

Cristian Crețu 17 Dec 31, 2022
A tiny utility library to generate mesh gradient based on 4 RGB colors, built with vanilla js.

MeshGradient.js mesh-gradient.js is tiny utility library to generate mesh gradient based on 4 RGB colors, built with vanilla js. Installation! npm ins

Anup Aglawe 7 Jan 4, 2023
Library for controlling MESH blocks using JavaScript.

MESH.js Library for controlling MESH blocks using JavaScript. MESH official web site is here. Verified Environment Node.js (16.15.1) npm (8.11.0) Prer

MESH project 6 Nov 22, 2022
A mesh network that connects remote communities to emergency responders without relying on internet, cell towers, or satellites

A mesh network that connects remote communities to emergency responders without relying on internet, cell towers, or satellites. Winner of Hack the North 2022. ??

Alice Cai 4 Sep 23, 2022
Lightweight, Portable, Flexible Distributed/Mobile Deep Learning with Dynamic, Mutation-aware Dataflow Dep Scheduler; for Python, R, Julia, Scala, Go, Javascript and more

Apache MXNet (incubating) for Deep Learning Apache MXNet is a deep learning framework designed for both efficiency and flexibility. It allows you to m

The Apache Software Foundation 20.2k Jan 5, 2023
three.js examples. if you are first in learning three.js , this will give you much help.

three-projected-material Three.js Material which lets you do Texture Projection on a 3d Model. Installation After having installed three.js, install i

null 22 Nov 2, 2022
Intuitive and dynamic Chrome Dev Tool Extension for Three.js debugging

sceneSniff An intuitive and dynamic Chrome Dev Tool Extension for Three.js debugging sceneSniff is an in browser developer tool for Three.js projects.

OSLabs Beta 59 Dec 8, 2022
GPU supercharged attraction-graph visualizations built on top of Three.js

Force Directed Graph GPU supercharged attraction-graph visualizations for the web built on top of Three.js. Importable as an ES6 module. Simulation co

Jono Brandel 55 Dec 9, 2022
Dynamic-web-development - Dynamic web development used CSS and HTML

Dynamic-web-development ASSISNMENT I just used CSS and HTML to make a mobile int

null 1 Feb 8, 2022
A lightweight, performant, and simple-to-use wrapper component to stick section headers to the top when scrolling brings them to top

A lightweight, performant, and simple-to-use wrapper component to stick section headers to the top when scrolling brings them to top

Mayank 7 Jun 27, 2022
dynamic-component-app is an angular application for dynamic component template creation

MyApp This project was generated with Angular CLI version 14.1.0. Development server Run ng serve for a dev server. Navigate to http://localhost:4200/

Aniket Muruskar 7 Aug 26, 2022
a simple wrapper nestjs dynamic module on top of surrealdb.js driver, with a consumer app to show case library in action, nothing fancy

README README Project Components Dynamic Module Consumer App Install SurrealDb Starts SurrealDb Init surrealDb Database Run App from Source Code Launc

Mário Monteiro 0 Oct 3, 2022
🌌 Fast, in-memory, full-text search engine written in TypeScript. Now in beta.

Installation You can install Lyra using npm, yarn, pnpm: npm i @nearform/lyra yarn add @nearform/lyra pnpm add @nearform/lyra Usage Lyra is quite simp

NearForm 5.1k Dec 30, 2022
A dependency-free JavaScript ES6 slider and carousel. It’s lightweight, flexible and fast. Designed to slide. No less, no more

Glide.js is a dependency-free JavaScript ES6 slider and carousel. It’s lightweight, flexible and fast. Designed to slide. No less, no more What can co

null 6.7k Jan 3, 2023
Kafka 0.8.0 broker implementation on top of Cloudflare Workers

Kafka Worker A Kafka 0.8.0 broker implementation on top of Cloudflare Workers and Durable Objects. This broker supports 4 client-facing APIs: Produce

Max Peterson 129 Dec 11, 2022
A fast, portable, flexible JavaScript component framework

SAN 一个快速、轻量、灵活的 JavaScript 组件框架 A fast, portable, flexible JavaScript component framework. HomePage 网站 安装(Install) NPM: $ npm i san CDN: <script src=

Baidu 4.6k Dec 29, 2022