Skip to content

Commit 36653c9

Browse files
committed
updated to new lightgl.js version
1 parent caf6854 commit 36653c9

File tree

8 files changed

+585
-336
lines changed

8 files changed

+585
-336
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
![](http://evanw.github.com/csg.js/csg.png)
44

5-
Constructive Solid Geometry (CSG) is a modeling technique that uses Boolean operations like union and intersection to combine 3D solids. This library implements CSG operations elegantly and concisely using BSP trees, and is meant to serve as an easily understandable implementation of the algorithm. All edge cases involving overlapping coplanar polygons in both solids are correctly handled.
5+
Constructive Solid Geometry (CSG) is a modeling technique that uses Boolean operations like union and intersection to combine 3D solids. This library implements CSG operations on meshes elegantly and concisely using BSP trees, and is meant to serve as an easily understandable implementation of the algorithm. All edge cases involving overlapping coplanar polygons in both solids are correctly handled.
66

77
Example usage:
88

@@ -16,7 +16,7 @@ Example usage:
1616

1717
# Demos
1818

19-
* [CSG example from Wikipedia](http://evanw.github.com/csg.js/tests/wikipedia.html)
19+
* [All CSG operations](http://evanw.github.com/csg.js/tests/intro.html)
2020
* [Coplanar polygon test cases](http://evanw.github.com/csg.js/tests/coplanar.html)
2121

2222
# Implementation Details

csg.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
// Constructive Solid Geometry (CSG) is a modeling technique that uses Boolean
22
// operations like union and intersection to combine 3D solids. This library
3-
// implements CSG operations elegantly and concisely using BSP trees, and is
4-
// meant to serve as an easily understandable implementation of the algorithm.
5-
// All edge cases involving overlapping coplanar polygons in both solids are
6-
// correctly handled.
3+
// implements CSG operations on meshes elegantly and concisely using BSP trees,
4+
// and is meant to serve as an easily understandable implementation of the
5+
// algorithm. All edge cases involving overlapping coplanar polygons in both
6+
// solids are correctly handled.
77
//
88
// Example usage:
99
//

tests/coplanar.html

Lines changed: 262 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,262 @@
1-
<html>
2-
<body style="margin:0;overflow:hidden;">
3-
<script src="../csg.js"></script>
4-
<script src="lightgl.js"></script>
5-
<script src="coplanar.js"></script>
6-
</body>
7-
</html>
1+
<!DOCTYPE html>
2+
<html><head>
3+
<script src="lightgl.js"></script>
4+
<script src="../csg.js"></script>
5+
<style>
6+
7+
body {
8+
font: 14px/20px 'Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, Arial, sans-serif;
9+
max-width: 800px;
10+
margin: 0 auto;
11+
padding: 50px 50px 200px 50px;
12+
}
13+
14+
pre, code {
15+
font: 12px/20px Monaco, monospace;
16+
border: 1px solid #CCC;
17+
border-radius: 3px;
18+
background: #F9F9F9;
19+
padding: 0 3px;
20+
color: #555;
21+
}
22+
pre { padding: 10px; }
23+
24+
h1, h2 { font: bold 50px/50px 'Helvetica Neue', Helvetica, Arial; }
25+
h2 { font-size: 30px; margin: 100px 0 0 0; }
26+
a { color: inherit; }
27+
.viewer { width: 200px; height: 200px; background: #EEE; }
28+
table { border-collapse: collapse; margin: 0 auto; }
29+
td { padding: 5px; text-align: center; }
30+
td code { background: none; border: none; color: inherit; }
31+
canvas { cursor: move; }
32+
33+
</style>
34+
</head><body>
35+
<h1>csg.js</h1>
36+
<p><b>Source code:</b> <a href="http://github.com/evanw/csg.js/">http://github.com/evanw/csg.js/</a><br>
37+
<b>Documentation:</b> <a href="http://evanw.github.com/csg.js/docs/">http://evanw.github.com/csg.js/docs/</a></p>
38+
39+
<h2>Coplanar Test Cases</h2>
40+
<p>These tests cover all the cases of overlapping coplanar polygons in both solids. To avoid duplicate polygons, the overlapping polygons are kept in the first solid and removed from the second solid.</p>
41+
<table>
42+
<tr>
43+
<td><div id="0" class="viewer"></div><code>a</code></td>
44+
<td><div id="1" class="viewer"></div><code>b</code></td>
45+
</tr>
46+
<tr>
47+
<td><div id="2" class="viewer"></div><code>a.inverse()</code></td>
48+
<td><div id="3" class="viewer"></div><code>b.inverse()</code></td>
49+
</tr>
50+
<tr>
51+
<td><div id="4" class="viewer"></div><code>a.union(b)</code></td>
52+
<td><div id="5" class="viewer"></div><code>b.union(a)</code></td>
53+
</tr>
54+
<tr>
55+
<td><div id="6" class="viewer"></div><code>a.subtract(b)</code></td>
56+
<td><div id="7" class="viewer"></div><code>b.subtract(a)</code></td>
57+
</tr>
58+
<tr>
59+
<td><div id="8" class="viewer"></div><code>a.intersect(b)</code></td>
60+
<td><div id="9" class="viewer"></div><code>b.intersect(a)</code></td>
61+
</tr>
62+
</table>
63+
<table>
64+
<tr>
65+
<td><div id="10" class="viewer"></div><code>c</code></td>
66+
<td><div id="11" class="viewer"></div><code>d</code></td>
67+
</tr>
68+
<tr>
69+
<td><div id="12" class="viewer"></div><code>c.inverse()</code></td>
70+
<td><div id="13" class="viewer"></div><code>d.inverse()</code></td>
71+
</tr>
72+
<tr>
73+
<td><div id="14" class="viewer"></div><code>c.union(d)</code></td>
74+
<td><div id="15" class="viewer"></div><code>d.union(c)</code></td>
75+
</tr>
76+
<tr>
77+
<td><div id="16" class="viewer"></div><code>c.subtract(d)</code></td>
78+
<td><div id="17" class="viewer"></div><code>d.subtract(c)</code></td>
79+
</tr>
80+
<tr>
81+
<td><div id="18" class="viewer"></div><code>c.intersect(d)</code></td>
82+
<td><div id="19" class="viewer"></div><code>d.intersect(c)</code></td>
83+
</tr>
84+
</table>
85+
86+
<script>
87+
88+
// Set the color of all polygons in this solid
89+
CSG.prototype.setColor = function(r, g, b) {
90+
this.toPolygons().map(function(polygon) {
91+
polygon.shared = [r, g, b];
92+
});
93+
};
94+
95+
// Convert from CSG solid to GL.Mesh object
96+
CSG.prototype.toMesh = function() {
97+
var mesh = new GL.Mesh({ normals: true, colors: true });
98+
var indexer = new GL.Indexer();
99+
this.toPolygons().map(function(polygon) {
100+
var indices = polygon.vertices.map(function(vertex) {
101+
vertex.color = polygon.shared;
102+
return indexer.add(vertex);
103+
});
104+
for (var i = 2; i < indices.length; i++) {
105+
mesh.triangles.push([indices[0], indices[i - 1], indices[i]]);
106+
}
107+
});
108+
mesh.vertices = indexer.unique.map(function(v) { return [v.pos.x, v.pos.y, v.pos.z]; });
109+
mesh.normals = indexer.unique.map(function(v) { return [v.normal.x, v.normal.y, v.normal.z]; });
110+
mesh.colors = indexer.unique.map(function(v) { return v.color; });
111+
mesh.computeWireframe();
112+
return mesh;
113+
};
114+
115+
var angleX = 20;
116+
var angleY = 20;
117+
var viewers = [];
118+
119+
// A viewer is a WebGL canvas that lets the user view a mesh. The user can
120+
// tumble it around by dragging the mouse.
121+
function Viewer(csg, width, height, depth) {
122+
viewers.push(this);
123+
124+
// Get a new WebGL canvas
125+
var gl = GL.create();
126+
this.gl = gl;
127+
this.mesh = csg.toMesh();
128+
129+
// Set up the viewport
130+
gl.canvas.width = width;
131+
gl.canvas.height = height;
132+
gl.viewport(0, 0, width, height);
133+
gl.matrixMode(gl.PROJECTION);
134+
gl.loadIdentity();
135+
gl.perspective(45, width / height, 0.1, 100);
136+
gl.matrixMode(gl.MODELVIEW);
137+
138+
// Set up WebGL state
139+
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
140+
gl.clearColor(0.93, 0.93, 0.93, 1);
141+
gl.enable(gl.DEPTH_TEST);
142+
gl.enable(gl.CULL_FACE);
143+
gl.polygonOffset(1, 1);
144+
145+
// Black shader for wireframe
146+
this.blackShader = new GL.Shader('\
147+
void main() {\
148+
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\
149+
}\
150+
', '\
151+
void main() {\
152+
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.3);\
153+
}\
154+
');
155+
156+
// Shader with diffuse and specular lighting
157+
this.lightingShader = new GL.Shader('\
158+
varying vec3 color;\
159+
varying vec3 normal;\
160+
varying vec3 light;\
161+
void main() {\
162+
const vec3 lightDir = vec3(1.0, 2.0, 3.0) / 3.741657386773941;\
163+
light = (gl_ModelViewMatrix * vec4(lightDir, 0.0)).xyz;\
164+
color = gl_Color.rgb;\
165+
normal = gl_NormalMatrix * gl_Normal;\
166+
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\
167+
}\
168+
', '\
169+
varying vec3 color;\
170+
varying vec3 normal;\
171+
varying vec3 light;\
172+
void main() {\
173+
vec3 n = normalize(normal);\
174+
float diffuse = max(0.0, dot(light, n));\
175+
float specular = pow(max(0.0, -reflect(light, n).z), 32.0) * sqrt(diffuse);\
176+
gl_FragColor = vec4(mix(color * (0.3 + 0.7 * diffuse), vec3(1.0), specular), 1.0);\
177+
}\
178+
');
179+
180+
gl.onmousemove = function(e) {
181+
if (e.dragging) {
182+
angleY += e.deltaX * 2;
183+
angleX += e.deltaY * 2;
184+
angleX = Math.max(-90, Math.min(90, angleX));
185+
186+
viewers.map(function(viewer) {
187+
viewer.gl.ondraw();
188+
});
189+
}
190+
};
191+
192+
var that = this;
193+
gl.ondraw = function() {
194+
gl.makeCurrent();
195+
196+
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
197+
gl.loadIdentity();
198+
gl.translate(0, 0, -depth);
199+
gl.rotate(angleX, 1, 0, 0);
200+
gl.rotate(angleY, 0, 1, 0);
201+
202+
gl.enable(gl.POLYGON_OFFSET_FILL);
203+
that.lightingShader.draw(that.mesh, gl.TRIANGLES);
204+
gl.disable(gl.POLYGON_OFFSET_FILL);
205+
206+
gl.enable(gl.BLEND);
207+
that.blackShader.draw(that.mesh, gl.LINES);
208+
gl.disable(gl.BLEND);
209+
};
210+
211+
gl.ondraw();
212+
}
213+
214+
var nextID = 0;
215+
function addViewer(viewer) {
216+
document.getElementById(nextID++).appendChild(viewer.gl.canvas);
217+
}
218+
219+
// Test all coplanar cases
220+
var a = CSG.cube();
221+
var b = CSG.cylinder({ slices: 8, start: new CSG.Vector(-1, 0, 0), end: new CSG.Vector(1, 0, 0) });
222+
b = b.union(CSG.cylinder({ slices: 8, start: new CSG.Vector(0, -1, 0), end: new CSG.Vector(0, 1, 0) }));
223+
b = b.union(CSG.cylinder({ slices: 8, start: new CSG.Vector(0, 0, -1), end: new CSG.Vector(0, 0, 1) }));
224+
var c = CSG.cube({ radius: 0.5 });
225+
var d = CSG.cylinder({ radius: 0.5, slices: 8, start: new CSG.Vector(-1, 0, 0), end: new CSG.Vector(-0.5, 0, 0) });
226+
d = d.union(CSG.cylinder({ radius: 0.5, slices: 8, start: new CSG.Vector(0.5, 0, 0), end: new CSG.Vector(1, 0, 0) }));
227+
d = d.union(CSG.cylinder({ radius: 0.5, slices: 8, start: new CSG.Vector(0, -1, 0), end: new CSG.Vector(0, -0.5, 0) }));
228+
d = d.union(CSG.cylinder({ radius: 0.5, slices: 8, start: new CSG.Vector(0, 0.5, 0), end: new CSG.Vector(0, 1, 0) }));
229+
d = d.union(CSG.cylinder({ radius: 0.5, slices: 8, start: new CSG.Vector(0, 0, -1), end: new CSG.Vector(0, 0, -0.5) }));
230+
d = d.union(CSG.cylinder({ radius: 0.5, slices: 8, start: new CSG.Vector(0, 0, 0.5), end: new CSG.Vector(0, 0, 1) }));
231+
a.setColor(1, 0, 0);
232+
b.setColor(0, 0, 1);
233+
c.setColor(0, 1, 0);
234+
d.setColor(1, 1, 0);
235+
var operations = [
236+
a,
237+
b,
238+
a.inverse(),
239+
b.inverse(),
240+
a.union(b),
241+
b.union(a),
242+
a.subtract(b),
243+
b.subtract(a),
244+
a.intersect(b),
245+
b.intersect(a),
246+
c,
247+
d,
248+
c.inverse(),
249+
d.inverse(),
250+
c.union(d),
251+
d.union(c),
252+
c.subtract(d),
253+
d.subtract(c),
254+
c.intersect(d),
255+
d.intersect(c)
256+
];
257+
for (var i = 0; i < operations.length; i++) {
258+
addViewer(new Viewer(operations[i], 200, 200, 6));
259+
}
260+
261+
</script>
262+
</body></html>

0 commit comments

Comments
 (0)