@niivue/niivue 0.22.2 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,27 +1,47 @@
1
- import * as mat from "gl-matrix";
1
+ import { mat4, vec3 } from "gl-matrix";
2
2
 
3
+ /**
4
+ * @class NiivueObject3D
5
+ * @type NiivueObject3D
6
+ * @typedef NiivueObject3D
7
+ * @property {Shader[]} renderShaders
8
+ * @property {boolean} isVisible
9
+ * @property {WebGLVertexArrayObject} vertexBuffer
10
+ * @property {number} indexCount
11
+ * @property {WebGLVertexArrayObject} indexBuffer
12
+ * @property {WebGLVertexArrayObject} textureCoordinateBuffer
13
+ * @property {number} mode
14
+ * @description Object rendered with WebGL
15
+ * @constructor
16
+ * @param {number} id
17
+ * @param {WebGLVertexArrayObject} vertexBuffer
18
+ * @param {number} mode
19
+ * @param {number} indexCount
20
+ * @param {WebGLVertexArrayObject} indexBuffer
21
+ * @param {WebGLVertexArrayObject} textureCoordinateBuffer
22
+ **/
3
23
  export var NiivueObject3D = function (
4
24
  id,
5
25
  vertexBuffer,
6
26
  mode,
7
27
  indexCount,
8
28
  indexBuffer = null,
9
- textureCoordinateBuffer = null
29
+ vao = null
10
30
  ) {
11
31
  this.BLEND = 1;
12
32
  this.CULL_FACE = 2;
13
33
  this.CULL_FRONT = 4;
14
34
  this.CULL_BACK = 8;
15
35
  this.ENABLE_DEPTH_TEST = 16;
16
-
36
+ this.sphereIdx = [];
37
+ this.sphereVtx = [];
17
38
  this.renderShaders = [];
18
- this.pickingShader = null;
19
39
  this.isVisible = true;
20
40
  this.isPickable = true;
21
41
  this.vertexBuffer = vertexBuffer;
22
42
  this.indexCount = indexCount;
23
43
  this.indexBuffer = indexBuffer;
24
- this.textureCoordinateBuffer = textureCoordinateBuffer;
44
+ this.vao = vao;
25
45
  this.mode = mode;
26
46
 
27
47
  this.glFlags = 0;
@@ -33,7 +53,7 @@ export var NiivueObject3D = function (
33
53
  ((id >> 24) & 0xff) / 255.0,
34
54
  ];
35
55
 
36
- this.modelMatrix = mat.mat4.create();
56
+ this.modelMatrix = mat4.create();
37
57
  this.scale = [1, 1, 1];
38
58
  this.position = [0, 0, 0];
39
59
  this.rotation = [0, 0, 0];
@@ -65,7 +85,8 @@ NiivueObject3D.generateCrosshairs = function (
65
85
  geometry.vertexBuffer,
66
86
  gl.TRIANGLES,
67
87
  geometry.indexCount,
68
- geometry.indexBuffer
88
+ geometry.indexBuffer,
89
+ geometry.vao
69
90
  );
70
91
  };
71
92
 
@@ -80,14 +101,14 @@ NiivueObject3D.generateCrosshairsGeometry = function (
80
101
  ) {
81
102
  let vertices = [];
82
103
  let indices = [];
83
- let start = mat.vec3.fromValues(xyzMin[0], xyzMM[1], xyzMM[2]);
84
- let dest = mat.vec3.fromValues(xyzMax[0], xyzMM[1], xyzMM[2]);
104
+ let start = vec3.fromValues(xyzMin[0], xyzMM[1], xyzMM[2]);
105
+ let dest = vec3.fromValues(xyzMax[0], xyzMM[1], xyzMM[2]);
85
106
  NiivueObject3D.makeCylinder(vertices, indices, start, dest, radius, sides);
86
- start = mat.vec3.fromValues(xyzMM[0], xyzMin[1], xyzMM[2]);
87
- dest = mat.vec3.fromValues(xyzMM[0], xyzMax[1], xyzMM[2]);
107
+ start = vec3.fromValues(xyzMM[0], xyzMin[1], xyzMM[2]);
108
+ dest = vec3.fromValues(xyzMM[0], xyzMax[1], xyzMM[2]);
88
109
  NiivueObject3D.makeCylinder(vertices, indices, start, dest, radius, sides);
89
- start = mat.vec3.fromValues(xyzMM[0], xyzMM[1], xyzMin[2]);
90
- dest = mat.vec3.fromValues(xyzMM[0], xyzMM[1], xyzMax[2]);
110
+ start = vec3.fromValues(xyzMM[0], xyzMM[1], xyzMin[2]);
111
+ dest = vec3.fromValues(xyzMM[0], xyzMM[1], xyzMax[2]);
91
112
  NiivueObject3D.makeCylinder(vertices, indices, start, dest, radius, sides);
92
113
  //console.log('i:',indices.length / 3, 'v:',vertices.length / 3);
93
114
 
@@ -100,19 +121,29 @@ NiivueObject3D.generateCrosshairsGeometry = function (
100
121
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
101
122
  gl.bufferData(
102
123
  gl.ELEMENT_ARRAY_BUFFER,
103
- new Uint16Array(indices),
124
+ new Uint32Array(indices),
104
125
  gl.STATIC_DRAW
105
126
  );
106
127
 
128
+ let vao = gl.createVertexArray();
129
+ gl.bindVertexArray(vao);
130
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
131
+ gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
132
+ //vertex position: 3 floats X,Y,Z
133
+ gl.enableVertexAttribArray(0);
134
+ gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
135
+ gl.bindVertexArray(null); // https://stackoverflow.com/questions/43904396/are-we-not-allowed-to-bind-gl-array-buffer-and-vertex-attrib-array-to-0-in-webgl
136
+
107
137
  return {
108
138
  vertexBuffer,
109
139
  indexBuffer,
110
140
  indexCount: indices.length,
141
+ vao,
111
142
  };
112
143
  };
113
144
 
114
145
  NiivueObject3D.getFirstPerpVector = function (v1) {
115
- let v2 = mat.vec3.fromValues(0.0, 0.0, 0.0);
146
+ let v2 = vec3.fromValues(0.0, 0.0, 0.0);
116
147
  if (v1[0] === 0.0) v2[0] = 1.0;
117
148
  else if (v1[1] === 0.0) v2[1] = 1.0;
118
149
  else if (v1[2] === 0.0) v2[2] = 1.0;
@@ -122,37 +153,175 @@ NiivueObject3D.getFirstPerpVector = function (v1) {
122
153
  v2[0] = v1[2]; //scalp = z*x
123
154
  v2[1] = v1[2]; //scalp = z*(x+y)
124
155
  v2[2] = -(v1[0] + v1[1]); //scalp = z*(x+y)-z*(x+y) = 0
125
- mat.vec3.normalize(v2, v2);
156
+ vec3.normalize(v2, v2);
126
157
  }
127
158
  return v2;
128
159
  };
129
160
 
161
+ NiivueObject3D.subdivide = function (verts, faces) {
162
+ //Subdivide each triangle into four triangles, pushing verts to the unit sphere"""
163
+ let nv = verts.length / 3;
164
+ let nf = faces.length / 3;
165
+ let n = nf;
166
+ let vNew = vec3.create();
167
+ let nNew = vec3.create();
168
+ for (let faceIndex = 0; faceIndex < n; faceIndex++) {
169
+ //setlength(verts, nv + 3);
170
+ let fx = faces[faceIndex * 3 + 0];
171
+ let fy = faces[faceIndex * 3 + 1];
172
+ let fz = faces[faceIndex * 3 + 2];
173
+ let vx = vec3.fromValues(
174
+ verts[fx * 3 + 0],
175
+ verts[fx * 3 + 1],
176
+ verts[fx * 3 + 2]
177
+ );
178
+ let vy = vec3.fromValues(
179
+ verts[fy * 3 + 0],
180
+ verts[fy * 3 + 1],
181
+ verts[fy * 3 + 2]
182
+ );
183
+ let vz = vec3.fromValues(
184
+ verts[fz * 3 + 0],
185
+ verts[fz * 3 + 1],
186
+ verts[fz * 3 + 2]
187
+ );
188
+ vec3.add(vNew, vx, vy);
189
+ vec3.normalize(nNew, vNew);
190
+ verts.push(...nNew);
191
+
192
+ vec3.add(vNew, vy, vz);
193
+ vec3.normalize(nNew, vNew);
194
+ verts.push(...nNew);
195
+
196
+ vec3.add(vNew, vx, vz);
197
+ vec3.normalize(nNew, vNew);
198
+ verts.push(...nNew);
199
+ //Split the current triangle into four smaller triangles:
200
+ let face = [nv, nv + 1, nv + 2];
201
+ faces.push(...face);
202
+ face = [fx, nv, nv + 2];
203
+ faces.push(...face);
204
+ face = [nv, fy, nv + 1];
205
+ faces.push(...face);
206
+ faces[faceIndex * 3 + 0] = nv + 2;
207
+ faces[faceIndex * 3 + 1] = nv + 1;
208
+ faces[faceIndex * 3 + 2] = fz;
209
+ nf = nf + 3;
210
+ nv = nv + 3;
211
+ }
212
+ };
213
+
214
+ NiivueObject3D.weldVertices = function (verts, faces) {
215
+ //unify identical vertices
216
+ let nv = verts.length / 3;
217
+ //yikes: bubble sort! TO DO: see Surfice for more efficient solution
218
+ let nUnique = 0; //first vertex is unique
219
+ //var remap = new Array();
220
+ let remap = new Int32Array(nv);
221
+ for (let i = 0; i < nv - 1; i++) {
222
+ if (remap[i] !== 0) continue; //previously tested
223
+ remap[i] = nUnique;
224
+ let v = i * 3;
225
+ let x = verts[v];
226
+ let y = verts[v + 1];
227
+ let z = verts[v + 2];
228
+ for (let j = i + 1; j < nv; j++) {
229
+ v += 3;
230
+ if (x === verts[v] && y === verts[v + 1] && z === verts[v + 2])
231
+ remap[j] = nUnique;
232
+ }
233
+ nUnique++; //another new vertex
234
+ } //for i
235
+ if (nUnique === nv) return verts;
236
+ //console.log('welding vertices removed redundant positions ', nv, '->', nUnique);
237
+ let nf = faces.length;
238
+ for (let f = 0; f < nf; f++) faces[f] = remap[faces[f]];
239
+ let vtx = verts.slice(0, nUnique * 3 - 1);
240
+ for (let i = 0; i < nv - 1; i++) {
241
+ let v = i * 3;
242
+ let r = remap[i] * 3;
243
+ vtx[r] = verts[v];
244
+ vtx[r + 1] = verts[v + 1];
245
+ vtx[r + 2] = verts[v + 2];
246
+ }
247
+ return vtx;
248
+ };
249
+
250
+ NiivueObject3D.makeSphere = function (
251
+ vertices,
252
+ indices,
253
+ radius,
254
+ origin = [0, 0, 0]
255
+ ) {
256
+ let vtx = [];
257
+ let idx = [];
258
+ if (this.sphereVtx !== undefined) {
259
+ //only generate unit sphere once...
260
+ vtx = this.sphereVtx.slice();
261
+ idx = this.sphereIdx.slice();
262
+ } else {
263
+ vtx = [
264
+ 0.0, 0.0, 1.0, 0.894, 0.0, 0.447, 0.276, 0.851, 0.447, -0.724, 0.526,
265
+ 0.447, -0.724, -0.526, 0.447, 0.276, -0.851, 0.447, 0.724, 0.526, -0.447,
266
+ -0.276, 0.851, -0.447, -0.894, 0.0, -0.447, -0.276, -0.851, -0.447, 0.724,
267
+ -0.526, -0.447, 0.0, 0.0, -1.0,
268
+ ];
269
+ //let idx = new Uint16Array([
270
+ idx = [
271
+ 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5, 0, 5, 1, 7, 6, 11, 8, 7, 11, 9, 8, 11,
272
+ 10, 9, 11, 6, 10, 11, 6, 2, 1, 7, 3, 2, 8, 4, 3, 9, 5, 4, 10, 1, 5, 6, 7,
273
+ 2, 7, 8, 3, 8, 9, 4, 9, 10, 5, 10, 6, 1,
274
+ ];
275
+ this.subdivide(vtx, idx);
276
+ this.subdivide(vtx, idx);
277
+ vtx = this.weldVertices(vtx, idx);
278
+ this.sphereVtx = vtx.slice();
279
+ this.sphereIdx = idx.slice();
280
+ }
281
+ for (let i = 0; i < vtx.length; i++) vtx[i] = vtx[i] * radius;
282
+ let nvtx = vtx.length / 3;
283
+ let j = 0;
284
+ for (let i = 0; i < nvtx; i++) {
285
+ vtx[j] = vtx[j] + origin[0];
286
+ j++;
287
+ vtx[j] = vtx[j] + origin[1];
288
+ j++;
289
+ vtx[j] = vtx[j] + origin[2];
290
+ j++;
291
+ }
292
+ let idx0 = Math.floor(vertices.length / 3); //first new vertex will be AFTER previous vertices
293
+ for (let i = 0; i < idx.length; i++) idx[i] = idx[i] + idx0;
294
+
295
+ indices.push(...idx);
296
+ vertices.push(...vtx);
297
+ };
298
+
130
299
  NiivueObject3D.makeCylinder = function (
131
300
  vertices,
132
301
  indices,
133
302
  start,
134
303
  dest,
135
304
  radius,
136
- sides = 20
305
+ sides = 20,
306
+ endcaps = true
137
307
  ) {
138
308
  if (sides < 3) sides = 3; //prism is minimal 3D cylinder
139
- let v1 = mat.vec3.create();
140
- mat.vec3.subtract(v1, dest, start);
141
- mat.vec3.normalize(v1, v1); //principle axis of cylinder
309
+ let v1 = vec3.create();
310
+ vec3.subtract(v1, dest, start);
311
+ vec3.normalize(v1, v1); //principle axis of cylinder
142
312
  let v2 = NiivueObject3D.getFirstPerpVector(v1); //a unit length vector orthogonal to v1
143
313
  // Get the second perp vector by cross product
144
- let v3 = mat.vec3.create();
145
- mat.vec3.cross(v3, v1, v2); //a unit length vector orthogonal to v1 and v2
146
- mat.vec3.normalize(v3, v3);
314
+ let v3 = vec3.create();
315
+ vec3.cross(v3, v1, v2); //a unit length vector orthogonal to v1 and v2
316
+ vec3.normalize(v3, v3);
147
317
  let num_v = 2 * sides;
148
318
  let num_f = 2 * sides;
149
- let endcaps = true;
150
319
  if (endcaps) {
151
320
  num_f += 2 * sides;
152
321
  num_v += 2;
153
322
  }
154
323
  let idx0 = Math.floor(vertices.length / 3); //first new vertex will be AFTER previous vertices
155
- let idx = new Uint16Array(num_f * 3);
324
+ let idx = new Uint32Array(num_f * 3);
156
325
  let vtx = new Float32Array(num_v * 3);
157
326
  function setV(i, vec3) {
158
327
  vtx[i * 3 + 0] = vec3[0];
@@ -170,17 +339,17 @@ NiivueObject3D.makeCylinder = function (
170
339
  setV(startPole, start);
171
340
  setV(destPole, dest);
172
341
  }
173
- let pt1 = mat.vec3.create();
174
- let pt2 = mat.vec3.create();
342
+ let pt1 = vec3.create();
343
+ let pt2 = vec3.create();
175
344
  for (let i = 0; i < sides; i++) {
176
345
  let c = Math.cos((i / sides) * 2 * Math.PI);
177
346
  let s = Math.sin((i / sides) * 2 * Math.PI);
178
347
  pt1[0] = radius * (c * v2[0] + s * v3[0]);
179
348
  pt1[1] = radius * (c * v2[1] + s * v3[1]);
180
349
  pt1[2] = radius * (c * v2[2] + s * v3[2]);
181
- mat.vec3.add(pt2, start, pt1);
350
+ vec3.add(pt2, start, pt1);
182
351
  setV(i, pt2);
183
- mat.vec3.add(pt2, dest, pt1);
352
+ vec3.add(pt2, dest, pt1);
184
353
  setV(i + sides, pt2);
185
354
  let nxt = 0;
186
355
  if (i < sides - 1) nxt = i + 1;
@@ -194,3 +363,48 @@ NiivueObject3D.makeCylinder = function (
194
363
  indices.push(...idx);
195
364
  vertices.push(...vtx);
196
365
  };
366
+
367
+ NiivueObject3D.makeColoredCylinder = function (
368
+ vertices,
369
+ indices,
370
+ colors,
371
+ start,
372
+ dest,
373
+ radius,
374
+ rgba255 = [192, 0, 0, 255],
375
+ sides = 20,
376
+ endcaps = false
377
+ ) {
378
+ let nv = vertices.length / 3;
379
+ this.makeCylinder(vertices, indices, start, dest, radius, sides, endcaps);
380
+ nv = vertices.length / 3 - nv;
381
+ let clrs = [];
382
+ for (let i = 0; i < nv * 4 - 1; i += 4) {
383
+ clrs[i] = rgba255[0];
384
+ clrs[i + 1] = rgba255[1];
385
+ clrs[i + 2] = rgba255[2];
386
+ clrs[i + 3] = rgba255[3];
387
+ }
388
+ colors.push(...clrs);
389
+ };
390
+
391
+ NiivueObject3D.makeColoredSphere = function (
392
+ vertices,
393
+ indices,
394
+ colors,
395
+ radius,
396
+ origin = [0, 0, 0],
397
+ rgba255 = [0, 0, 192, 255]
398
+ ) {
399
+ let nv = vertices.length / 3;
400
+ this.makeSphere(vertices, indices, radius, origin);
401
+ nv = vertices.length / 3 - nv;
402
+ let clrs = [];
403
+ for (let i = 0; i < nv * 4 - 1; i += 4) {
404
+ clrs[i] = rgba255[0];
405
+ clrs[i + 1] = rgba255[1];
406
+ clrs[i + 2] = rgba255[2];
407
+ clrs[i + 3] = rgba255[3];
408
+ }
409
+ colors.push(...clrs);
410
+ };
@@ -1,3 +1,9 @@
1
+ /**
2
+ * @class NiivueShader3D
3
+ * @type NiivueShader3D
4
+ * @constructor
5
+ * @param {Shader} shader
6
+ */
1
7
  export var NiivueShader3D = function (shader) {
2
8
  this.shader = shader;
3
9
  this.uniforms = shader.uniforms;