-
Notifications
You must be signed in to change notification settings - Fork 1
/
ShapeRenderer.hx
177 lines (153 loc) · 5.54 KB
/
ShapeRenderer.hx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
package;
import js.Browser;
import js.html.DivElement;
import js.three.CircleGeometry;
import js.three.Color;
import js.three.Face3;
import js.three.Geometry;
import js.three.Mesh;
import js.three.MeshBasicMaterial;
import js.three.Object3D;
import js.three.PerspectiveCamera;
import js.three.PlaneGeometry;
import js.three.Scene;
import js.three.Shape;
import js.three.ShapeGeometry;
import js.three.Vector3;
import js.three.WebGLRenderer;
import shape.Rgba;
import shape.Shape;
import shape.ShapeTypes;
import shape.abstracts.Circle;
import shape.abstracts.Ellipse;
import shape.abstracts.Rectangle;
import shape.abstracts.RotatedEllipse;
import shape.abstracts.RotatedRectangle;
import shape.abstracts.Triangle;
/**
* Code for rendering geometrized images with three.js.
* @author Sam Twidale (http://samcodes.co.uk/)
*/
@:keep
class ShapeRenderer {
var renderer:WebGLRenderer; // The three.js WebGL renderer
var scene:Scene; // The scene in which all the objects live
var shapesRoot:Object3D; // The root object for all the shapes added to the scene, direct children contain batches of shapes for each image
public var camera(default, null):PerspectiveCamera; // The camera view of the shapes
public function new(containerId:String) {
var container:DivElement = cast Browser.window.document.getElementById(containerId);
var canvas = Browser.window.document.createCanvasElement();
canvas.width = Browser.window.innerWidth;
canvas.height = Browser.window.innerHeight;
renderer = new WebGLRenderer({canvas:canvas, antialias:true});
renderer.sortObjects = false;
container.appendChild(renderer.domElement);
scene = new Scene();
scene.background = new Color(0x000000);
camera = new PerspectiveCamera(45, Browser.window.innerWidth / Browser.window.innerHeight, 1, 10000);
scene.add(camera);
shapesRoot = new Object3D();
scene.add(shapesRoot);
}
public function clearShapes() {
for (parent in shapesRoot.children) {
for (shape in parent.children) {
var m:Mesh = cast shape;
var g:Geometry = cast m.geometry;
g.dispose();
m.material.dispose();
}
}
scene.remove(shapesRoot);
this.shapesRoot = new Object3D();
scene.add(shapesRoot);
}
public function addShapes(shapes:Array<shape.Shape>):Object3D {
var shapesParent = new Object3D();
this.shapesRoot.add(shapesParent);
for (shape in shapes) {
var mesh:Mesh = switch(shape.type) {
case ShapeTypes.RECTANGLE:
addRectangle(shape.data, shape.color);
case ShapeTypes.ROTATED_RECTANGLE:
addRotatedRectangle(shape.data, shape.color);
case ShapeTypes.TRIANGLE:
addTriangle(shape.data, shape.color);
case ShapeTypes.ELLIPSE:
addEllipse(shape.data, shape.color);
case ShapeTypes.ROTATED_ELLIPSE:
addRotatedEllipse(shape.data, shape.color);
case ShapeTypes.CIRCLE:
addCircle(shape.data, shape.color);
case ShapeTypes.LINE, ShapeTypes.QUADRATIC_BEZIER, ShapeTypes.POLYLINE:
throw "Encountered unsupported shape type";
default:
throw "Encountered unsupported shape type";
};
shapesParent.add(mesh);
}
return shapesParent;
}
public function render():Void {
renderer.render(scene, camera);
}
public function resize(width:Int, height:Int) {
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height);
}
private inline function addRectangle(g:Rectangle, c:Rgba) {
var geometry = new PlaneGeometry(g.x2 - g.x1, g.y2 - g.y1);
var mesh = makeMesh(geometry, c);
mesh.position.set(g.x1 + ((g.x2 - g.x1) / 2), g.y1 + ((g.y2 - g.y1) / 2), 4.0);
return mesh;
}
private inline function addRotatedRectangle(g:RotatedRectangle, c:Rgba) {
var geometry = new PlaneGeometry(g.x2 - g.x1, g.y2 - g.y1);
var mesh = makeMesh(geometry, c);
mesh.rotation.z = g.angle * Math.PI / 180.0;
mesh.position.set(g.x1 + ((g.x2 - g.x1) / 2), g.y1 + ((g.y2 - g.y1) / 2), 4.0);
return mesh;
}
private inline function addTriangle(g:Triangle, c:Rgba) {
var geometry = new Geometry();
geometry.vertices.push(new Vector3(g.x1, g.y1, 0));
geometry.vertices.push(new Vector3(g.x2, g.y2, 0));
geometry.vertices.push(new Vector3(g.x3, g.y3, 0));
geometry.faces.push(new Face3(0, 1, 2));
var mesh = makeMesh(geometry, c);
return mesh;
}
private inline function addEllipse(g:Ellipse, c:Rgba) {
var path = new js.three.Shape();
path.absellipse(g.x, g.y, g.rx, g.ry, 0, 2 * Math.PI, true, 0);
var geometry = new ShapeGeometry(path);
var mesh = makeMesh(geometry, c);
return mesh;
}
private inline function addRotatedEllipse(g:RotatedEllipse, c:Rgba) {
var path = new js.three.Shape();
path.absellipse(g.x, g.y, g.rx, g.ry, 0, 2 * Math.PI, true, g.angle * (Math.PI/180));
var geometry = new ShapeGeometry(path);
var mesh = makeMesh(geometry, c);
return mesh;
}
private inline function addCircle(g:Circle, c:Rgba) {
var geometry = new CircleGeometry(g.r, 16);
var mesh = makeMesh(geometry, c);
mesh.position.set(g.x, g.y, 4.0);
return mesh;
}
// Creates a material from an RGBA8888 color value
private static inline function makeMaterial(color:Int):MeshBasicMaterial {
var rgb:Int = color >> 8;
var threeDoubleSide:Int = 2; // NOTE hackiness since generated threejs externs aren't working out of the box
var opacity:Float = (color & 0xFF) / 255.0;
return new MeshBasicMaterial({color:rgb, transparent:true, opacity:opacity, side:cast threeDoubleSide, depthTest:false});
}
private inline function makeMesh(geometry:Geometry, color:Int):Mesh {
var material = makeMaterial(color);
var mesh = new Mesh(geometry, material);
return mesh;
}
}