UNPKG

33.8 kBJavaScriptView Raw
1console.warn( "THREE.LightningStrike: As part of the transition to ES6 Modules, the files in 'examples/js' were deprecated in May 2020 (r117) and will be deleted in December 2020 (r124). You can find more information about developing using ES6 Modules in https://threejs.org/docs/#manual/en/introduction/Installation." );
2/**
3 * @fileoverview LightningStrike object for creating lightning strikes and voltaic arcs.
4 *
5 *
6 * Usage
7 *
8 * var myRay = new THREE.LightningStrike( paramsObject );
9 * var myRayMesh = new THREE.Mesh( myRay, myMaterial );
10 * scene.add( myRayMesh );
11 * ...
12 * myRay.update( currentTime );
13 *
14 * The "currentTime" can vary its rate, go forwards, backwards or even jump, but it cannot be negative.
15 *
16 * You should normally leave the ray position to (0, 0, 0). You should control it by changing the sourceOffset and destOffset parameters.
17 *
18 *
19 * LightningStrike parameters
20 *
21 * The paramsObject can contain any of the following parameters.
22 *
23 * Legend:
24 * 'LightningStrike' (also called 'ray'): An independent voltaic arc with its ramifications and defined with a set of parameters.
25 * 'Subray': A ramification of the ray. It is not a LightningStrike object.
26 * 'Segment': A linear segment piece of a subray.
27 * 'Leaf segment': A ray segment which cannot be smaller.
28 *
29 *
30 * The following parameters can be changed any time and if they vary smoothly, the ray form will also change smoothly:
31 *
32 * @param {Vector3} sourceOffset The point where the ray starts.
33 *
34 * @param {Vector3} destOffset The point where the ray ends.
35 *
36 * @param {double} timeScale The rate at wich the ray form changes in time. Default: 1
37 *
38 * @param {double} roughness From 0 to 1. The higher the value, the more wrinkled is the ray. Default: 0.9
39 *
40 * @param {double} straightness From 0 to 1. The higher the value, the more straight will be a subray path. Default: 0.7
41 *
42 * @param {Vector3} up0 Ray 'up' direction at the ray starting point. Must be normalized. It should be perpendicular to the ray forward direction but it doesn't matter much.
43 *
44 * @param {Vector3} up1 Like the up0 parameter but at the end of the ray. Must be normalized.
45 *
46 * @param {double} radius0 Radius of the main ray trunk at the start point. Default: 1
47 *
48 * @param {double} radius1 Radius of the main ray trunk at the end point. Default: 1
49 *
50 * @param {double} radius0Factor The radius0 of a subray is this factor times the radius0 of its parent subray. Default: 0.5
51 *
52 * @param {double} radius1Factor The radius1 of a subray is this factor times the radius1 of its parent subray. Default: 0.2
53 *
54 * @param {minRadius} Minimum value a subray radius0 or radius1 can get. Default: 0.1
55 *
56 *
57 * The following parameters should not be changed after lightning creation. They can be changed but the ray will change its form abruptly:
58 *
59 * @param {boolean} isEternal If true the ray never extinguishes. Otherwise its life is controlled by the 'birthTime' and 'deathTime' parameters. Default: true if any of those two parameters is undefined.
60 *
61 * @param {double} birthTime The time at which the ray starts its life and begins propagating. Only if isEternal is false. Default: None.
62 *
63 * @param {double} deathTime The time at which the ray ends vanishing and its life. Only if isEternal is false. Default: None.
64 *
65 * @param {double} propagationTimeFactor From 0 to 1. Lifetime factor at which the ray ends propagating and enters the steady phase. For example, 0.1 means it is propagating 1/10 of its lifetime. Default: 0.1
66 *
67 * @param {double} vanishingTimeFactor From 0 to 1. Lifetime factor at which the ray ends the steady phase and begins vanishing. For example, 0.9 means it is vanishing 1/10 of its lifetime. Default: 0.9
68 *
69 * @param {double} subrayPeriod Subrays cycle periodically. This is their time period. Default: 4
70 *
71 * @param {double} subrayDutyCycle From 0 to 1. This is the fraction of time a subray is active. Default: 0.6
72 *
73 *
74 * These parameters cannot change after lightning creation:
75 *
76 * @param {integer} maxIterations: Greater than 0. The number of ray's leaf segments is 2**maxIterations. Default: 9
77 *
78 * @param {boolean} isStatic Set to true only for rays which won't change over time and are not attached to moving objects (Rare case). It is used to set the vertex buffers non-dynamic. You can omit calling update() for these rays.
79 *
80 * @param {integer} ramification Greater than 0. Maximum number of child subrays a subray can have. Default: 5
81 *
82 * @param {integer} maxSubrayRecursion Greater than 0. Maximum level of recursion (subray descendant generations). Default: 3
83 *
84 * @param {double} recursionProbability From 0 to 1. The lower the value, the less chance each new generation of subrays has to generate new subrays. Default: 0.6
85 *
86 * @param {boolean} generateUVs If true, the ray geometry will have uv coordinates generated. u runs along the ray, and v across its perimeter. Default: false.
87 *
88 * @param {Object} randomGenerator Set here your random number generator which will seed the SimplexNoise and other decisions during ray tree creation.
89 * It can be used to generate repeatable rays. For that, set also the noiseSeed parameter, and each ray created with that generator and seed pair will be identical in time.
90 * The randomGenerator parameter should be an object with a random() function similar to Math.random, but seedable.
91 * It must have also a getSeed() method, which returns the current seed, and a setSeed( seed ) method, which accepts as seed a fractional number from 0 to 1, as well as any other number.
92 * The default value is an internal generator for some uses and Math.random for others (It is non-repeatable even if noiseSeed is supplied)
93 *
94 * @param {double} noiseSeed Seed used to make repeatable rays (see the randomGenerator)
95 *
96 * @param {function} onDecideSubrayCreation Set this to change the callback which decides subray creation. You can look at the default callback in the code (createDefaultSubrayCreationCallbacks)for more info.
97 *
98 * @param {function} onSubrayCreation This is another callback, more simple than the previous one. It can be used to adapt the form of subrays or other parameters once a subray has been created and initialized. It is used in the examples to adapt subrays to a sphere or to a plane.
99 *
100 *
101*/
102
103THREE.LightningStrike = function ( rayParameters ) {
104
105 THREE.BufferGeometry.call( this );
106
107 this.type = 'LightningStrike';
108
109 // Set parameters, and set undefined parameters to default values
110 rayParameters = rayParameters || {};
111 this.init( THREE.LightningStrike.copyParameters( rayParameters, rayParameters ) );
112
113 // Creates and populates the mesh
114 this.createMesh();
115
116};
117
118THREE.LightningStrike.prototype = Object.create( THREE.BufferGeometry.prototype );
119
120THREE.LightningStrike.prototype.constructor = THREE.LightningStrike;
121
122THREE.LightningStrike.prototype.isLightningStrike = true;
123
124// Ray states
125THREE.LightningStrike.RAY_INITIALIZED = 0;
126THREE.LightningStrike.RAY_UNBORN = 1;
127THREE.LightningStrike.RAY_PROPAGATING = 2;
128THREE.LightningStrike.RAY_STEADY = 3;
129THREE.LightningStrike.RAY_VANISHING = 4;
130THREE.LightningStrike.RAY_EXTINGUISHED = 5;
131
132THREE.LightningStrike.COS30DEG = Math.cos( 30 * Math.PI / 180 );
133THREE.LightningStrike.SIN30DEG = Math.sin( 30 * Math.PI / 180 );
134
135THREE.LightningStrike.createRandomGenerator = function () {
136
137 var numSeeds = 2053;
138 var seeds = [];
139
140 for ( var i = 0; i < numSeeds; i ++ ) {
141
142 seeds.push( Math.random() );
143
144 }
145
146 var generator = {
147
148 currentSeed: 0,
149
150 random: function () {
151
152 var value = seeds[ generator.currentSeed ];
153
154 generator.currentSeed = ( generator.currentSeed + 1 ) % numSeeds;
155
156 return value;
157
158 },
159
160 getSeed: function () {
161
162 return generator.currentSeed / numSeeds;
163
164 },
165
166 setSeed: function ( seed ) {
167
168 generator.currentSeed = Math.floor( seed * numSeeds ) % numSeeds;
169
170 }
171
172 };
173
174 return generator;
175
176};
177
178THREE.LightningStrike.copyParameters = function ( dest, source ) {
179
180 source = source || {};
181 dest = dest || {};
182
183 var vecCopy = function ( v ) {
184
185 if ( source === dest ) {
186
187 return v;
188
189 } else {
190
191 return v.clone();
192
193 }
194
195 };
196
197 dest.sourceOffset = source.sourceOffset !== undefined ? vecCopy( source.sourceOffset ) : new THREE.Vector3( 0, 100, 0 ),
198 dest.destOffset = source.destOffset !== undefined ? vecCopy( source.destOffset ) : new THREE.Vector3( 0, 0, 0 ),
199
200 dest.timeScale = source.timeScale !== undefined ? source.timeScale : 1,
201 dest.roughness = source.roughness !== undefined ? source.roughness : 0.9,
202 dest.straightness = source.straightness !== undefined ? source.straightness : 0.7,
203
204 dest.up0 = source.up0 !== undefined ? vecCopy( source.up0 ) : new THREE.Vector3( 0, 0, 1 );
205 dest.up1 = source.up1 !== undefined ? vecCopy( source.up1 ) : new THREE.Vector3( 0, 0, 1 ),
206 dest.radius0 = source.radius0 !== undefined ? source.radius0 : 1,
207 dest.radius1 = source.radius1 !== undefined ? source.radius1 : 1,
208 dest.radius0Factor = source.radius0Factor !== undefined ? source.radius0Factor : 0.5,
209 dest.radius1Factor = source.radius1Factor !== undefined ? source.radius1Factor : 0.2,
210 dest.minRadius = source.minRadius !== undefined ? source.minRadius : 0.2,
211
212 // These parameters should not be changed after lightning creation. They can be changed but the ray will change its form abruptly:
213
214 dest.isEternal = source.isEternal !== undefined ? source.isEternal : ( source.birthTime === undefined || source.deathTime === undefined ),
215 dest.birthTime = source.birthTime,
216 dest.deathTime = source.deathTime,
217 dest.propagationTimeFactor = source.propagationTimeFactor !== undefined ? source.propagationTimeFactor : 0.1,
218 dest.vanishingTimeFactor = source.vanishingTimeFactor !== undefined ? source.vanishingTimeFactor : 0.9,
219 dest.subrayPeriod = source.subrayPeriod !== undefined ? source.subrayPeriod : 4,
220 dest.subrayDutyCycle = source.subrayDutyCycle !== undefined ? source.subrayDutyCycle : 0.6;
221
222 // These parameters cannot change after lightning creation:
223
224 dest.maxIterations = source.maxIterations !== undefined ? source.maxIterations : 9;
225 dest.isStatic = source.isStatic !== undefined ? source.isStatic : false;
226 dest.ramification = source.ramification !== undefined ? source.ramification : 5;
227 dest.maxSubrayRecursion = source.maxSubrayRecursion !== undefined ? source.maxSubrayRecursion : 3;
228 dest.recursionProbability = source.recursionProbability !== undefined ? source.recursionProbability : 0.6;
229 dest.generateUVs = source.generateUVs !== undefined ? source.generateUVs : false;
230 dest.randomGenerator = source.randomGenerator,
231 dest.noiseSeed = source.noiseSeed,
232 dest.onDecideSubrayCreation = source.onDecideSubrayCreation,
233 dest.onSubrayCreation = source.onSubrayCreation;
234
235 return dest;
236
237};
238
239THREE.LightningStrike.prototype.update = function ( time ) {
240
241 if ( this.isStatic ) return;
242
243 if ( this.rayParameters.isEternal || ( this.rayParameters.birthTime <= time && time <= this.rayParameters.deathTime ) ) {
244
245 this.updateMesh( time );
246
247 if ( time < this.subrays[ 0 ].endPropagationTime ) {
248
249 this.state = THREE.LightningStrike.RAY_PROPAGATING;
250
251 } else if ( time > this.subrays[ 0 ].beginVanishingTime ) {
252
253 this.state = THREE.LightningStrike.RAY_VANISHING;
254
255 } else {
256
257 this.state = THREE.LightningStrike.RAY_STEADY;
258
259 }
260
261 this.visible = true;
262
263 } else {
264
265 this.visible = false;
266
267 if ( time < this.rayParameters.birthTime ) {
268
269 this.state = THREE.LightningStrike.RAY_UNBORN;
270
271 } else {
272
273 this.state = THREE.LightningStrike.RAY_EXTINGUISHED;
274
275 }
276
277 }
278
279};
280
281THREE.LightningStrike.prototype.init = function ( rayParameters ) {
282
283 // Init all the state from the parameters
284
285 this.rayParameters = rayParameters;
286
287 // These parameters cannot change after lightning creation:
288
289 this.maxIterations = rayParameters.maxIterations !== undefined ? Math.floor( rayParameters.maxIterations ) : 9;
290 rayParameters.maxIterations = this.maxIterations;
291 this.isStatic = rayParameters.isStatic !== undefined ? rayParameters.isStatic : false;
292 rayParameters.isStatic = this.isStatic;
293 this.ramification = rayParameters.ramification !== undefined ? Math.floor( rayParameters.ramification ) : 5;
294 rayParameters.ramification = this.ramification;
295 this.maxSubrayRecursion = rayParameters.maxSubrayRecursion !== undefined ? Math.floor( rayParameters.maxSubrayRecursion ) : 3;
296 rayParameters.maxSubrayRecursion = this.maxSubrayRecursion;
297 this.recursionProbability = rayParameters.recursionProbability !== undefined ? rayParameters.recursionProbability : 0.6;
298 rayParameters.recursionProbability = this.recursionProbability;
299 this.generateUVs = rayParameters.generateUVs !== undefined ? rayParameters.generateUVs : false;
300 rayParameters.generateUVs = this.generateUVs;
301
302 // Random generator
303 if ( rayParameters.randomGenerator !== undefined ) {
304
305 this.randomGenerator = rayParameters.randomGenerator;
306 this.seedGenerator = rayParameters.randomGenerator;
307
308 if ( rayParameters.noiseSeed !== undefined ) {
309
310 this.seedGenerator.setSeed( rayParameters.noiseSeed );
311
312 }
313
314 } else {
315
316 this.randomGenerator = THREE.LightningStrike.createRandomGenerator();
317 this.seedGenerator = Math;
318
319 }
320
321 // Ray creation callbacks
322 if ( rayParameters.onDecideSubrayCreation !== undefined ) {
323
324 this.onDecideSubrayCreation = rayParameters.onDecideSubrayCreation;
325
326 } else {
327
328 this.createDefaultSubrayCreationCallbacks();
329
330 if ( rayParameters.onSubrayCreation !== undefined ) {
331
332 this.onSubrayCreation = rayParameters.onSubrayCreation;
333
334 }
335
336 }
337
338 // Internal state
339
340 this.state = THREE.LightningStrike.RAY_INITIALIZED;
341
342 this.maxSubrays = Math.ceil( 1 + Math.pow( this.ramification, Math.max( 0, this.maxSubrayRecursion - 1 ) ) );
343 rayParameters.maxSubrays = this.maxSubrays;
344
345 this.maxRaySegments = 2 * ( 1 << this.maxIterations );
346
347 this.subrays = [];
348
349 for ( var i = 0; i < this.maxSubrays; i ++ ) {
350
351 this.subrays.push( this.createSubray() );
352
353 }
354
355 this.raySegments = [];
356
357 for ( var i = 0; i < this.maxRaySegments; i ++ ) {
358
359 this.raySegments.push( this.createSegment() );
360
361 }
362
363 this.time = 0;
364 this.timeFraction = 0;
365 this.currentSegmentCallback = null;
366 this.currentCreateTriangleVertices = this.generateUVs ? this.createTriangleVerticesWithUVs : this.createTriangleVerticesWithoutUVs;
367 this.numSubrays = 0;
368 this.currentSubray = null;
369 this.currentSegmentIndex = 0;
370 this.isInitialSegment = false;
371 this.subrayProbability = 0;
372
373 this.currentVertex = 0;
374 this.currentIndex = 0;
375 this.currentCoordinate = 0;
376 this.currentUVCoordinate = 0;
377 this.vertices = null;
378 this.uvs = null;
379 this.indices = null;
380 this.positionAttribute = null;
381 this.uvsAttribute = null;
382
383 this.simplexX = new THREE.SimplexNoise( this.seedGenerator );
384 this.simplexY = new THREE.SimplexNoise( this.seedGenerator );
385 this.simplexZ = new THREE.SimplexNoise( this.seedGenerator );
386
387 // Temp vectors
388 this.forwards = new THREE.Vector3();
389 this.forwardsFill = new THREE.Vector3();
390 this.side = new THREE.Vector3();
391 this.down = new THREE.Vector3();
392 this.middlePos = new THREE.Vector3();
393 this.middleLinPos = new THREE.Vector3();
394 this.newPos = new THREE.Vector3();
395 this.vPos = new THREE.Vector3();
396 this.cross1 = new THREE.Vector3();
397
398};
399
400THREE.LightningStrike.prototype.createMesh = function () {
401
402 var maxDrawableSegmentsPerSubRay = 1 << this.maxIterations;
403
404 var maxVerts = 3 * ( maxDrawableSegmentsPerSubRay + 1 ) * this.maxSubrays;
405 var maxIndices = 18 * maxDrawableSegmentsPerSubRay * this.maxSubrays;
406
407 this.vertices = new Float32Array( maxVerts * 3 );
408 this.indices = new Uint32Array( maxIndices );
409 if ( this.generateUVs ) {
410
411 this.uvs = new Float32Array( maxVerts * 2 );
412
413 }
414
415 // Populate the mesh
416 this.fillMesh( 0 );
417
418 this.setIndex( new THREE.Uint32BufferAttribute( this.indices, 1 ) );
419
420 this.positionAttribute = new THREE.Float32BufferAttribute( this.vertices, 3 );
421 this.setAttribute( 'position', this.positionAttribute );
422
423 if ( this.generateUVs ) {
424
425 this.uvsAttribute = new THREE.Float32BufferAttribute( new Float32Array( this.uvs ), 2 );
426 this.setAttribute( 'uv', this.uvsAttribute );
427
428 }
429
430 if ( ! this.isStatic ) {
431
432 this.index.usage = THREE.DynamicDrawUsage;
433 this.positionAttribute.usage = THREE.DynamicDrawUsage;
434 if ( this.generateUVs ) {
435
436 this.uvsAttribute.usage = THREE.DynamicDrawUsage;
437
438 }
439
440 }
441
442 // Store buffers for later modification
443 this.vertices = this.positionAttribute.array;
444 this.indices = this.index.array;
445 if ( this.generateUVs ) {
446
447 this.uvs = this.uvsAttribute.array;
448
449 }
450
451};
452
453THREE.LightningStrike.prototype.updateMesh = function ( time ) {
454
455 this.fillMesh( time );
456
457 this.drawRange.count = this.currentIndex;
458
459 this.index.needsUpdate = true;
460
461 this.positionAttribute.needsUpdate = true;
462
463 if ( this.generateUVs ) {
464
465 this.uvsAttribute.needsUpdate = true;
466
467 }
468
469};
470
471THREE.LightningStrike.prototype.fillMesh = function ( time ) {
472
473 var scope = this;
474
475 this.currentVertex = 0;
476 this.currentIndex = 0;
477 this.currentCoordinate = 0;
478 this.currentUVCoordinate = 0;
479
480 this.fractalRay( time, function fillVertices( segment ) {
481
482 var subray = scope.currentSubray;
483
484 if ( time < subray.birthTime ) { //&& ( ! this.rayParameters.isEternal || scope.currentSubray.recursion > 0 ) ) {
485
486 return;
487
488 } else if ( this.rayParameters.isEternal && scope.currentSubray.recursion == 0 ) {
489
490 // Eternal rays don't propagate nor vanish, but its subrays do
491
492 scope.createPrism( segment );
493
494 scope.onDecideSubrayCreation( segment, scope );
495
496 } else if ( time < subray.endPropagationTime ) {
497
498 if ( scope.timeFraction >= segment.fraction0 * subray.propagationTimeFactor ) {
499
500 // Ray propagation has arrived to this segment
501
502 scope.createPrism( segment );
503
504 scope.onDecideSubrayCreation( segment, scope );
505
506 }
507
508 } else if ( time < subray.beginVanishingTime ) {
509
510 // Ray is steady (nor propagating nor vanishing)
511
512 scope.createPrism( segment );
513
514 scope.onDecideSubrayCreation( segment, scope );
515
516 } else {
517
518 if ( scope.timeFraction <= subray.vanishingTimeFactor + segment.fraction1 * ( 1 - subray.vanishingTimeFactor ) ) {
519
520 // Segment has not yet vanished
521
522 scope.createPrism( segment );
523
524 }
525
526 scope.onDecideSubrayCreation( segment, scope );
527
528 }
529
530 } );
531
532};
533
534THREE.LightningStrike.prototype.addNewSubray = function ( /*rayParameters*/ ) {
535
536 return this.subrays[ this.numSubrays ++ ];
537
538};
539
540THREE.LightningStrike.prototype.initSubray = function ( subray, rayParameters ) {
541
542 subray.pos0.copy( rayParameters.sourceOffset );
543 subray.pos1.copy( rayParameters.destOffset );
544 subray.up0.copy( rayParameters.up0 );
545 subray.up1.copy( rayParameters.up1 );
546 subray.radius0 = rayParameters.radius0;
547 subray.radius1 = rayParameters.radius1;
548 subray.birthTime = rayParameters.birthTime;
549 subray.deathTime = rayParameters.deathTime;
550 subray.timeScale = rayParameters.timeScale;
551 subray.roughness = rayParameters.roughness;
552 subray.straightness = rayParameters.straightness;
553 subray.propagationTimeFactor = rayParameters.propagationTimeFactor;
554 subray.vanishingTimeFactor = rayParameters.vanishingTimeFactor;
555
556 subray.maxIterations = this.maxIterations;
557 subray.seed = rayParameters.noiseSeed !== undefined ? rayParameters.noiseSeed : 0;
558 subray.recursion = 0;
559
560};
561
562THREE.LightningStrike.prototype.fractalRay = function ( time, segmentCallback ) {
563
564 this.time = time;
565 this.currentSegmentCallback = segmentCallback;
566 this.numSubrays = 0;
567
568 // Add the top level subray
569 this.initSubray( this.addNewSubray(), this.rayParameters );
570
571 // Process all subrays that are being generated until consuming all of them
572 for ( var subrayIndex = 0; subrayIndex < this.numSubrays; subrayIndex ++ ) {
573
574 var subray = this.subrays[ subrayIndex ];
575 this.currentSubray = subray;
576
577 this.randomGenerator.setSeed( subray.seed );
578
579 subray.endPropagationTime = THREE.MathUtils.lerp( subray.birthTime, subray.deathTime, subray.propagationTimeFactor );
580 subray.beginVanishingTime = THREE.MathUtils.lerp( subray.deathTime, subray.birthTime, 1 - subray.vanishingTimeFactor );
581
582 var random1 = this.randomGenerator.random;
583 subray.linPos0.set( random1(), random1(), random1() ).multiplyScalar( 1000 );
584 subray.linPos1.set( random1(), random1(), random1() ).multiplyScalar( 1000 );
585
586 this.timeFraction = ( time - subray.birthTime ) / ( subray.deathTime - subray.birthTime );
587
588 this.currentSegmentIndex = 0;
589 this.isInitialSegment = true;
590
591 var segment = this.getNewSegment();
592 segment.iteration = 0;
593 segment.pos0.copy( subray.pos0 );
594 segment.pos1.copy( subray.pos1 );
595 segment.linPos0.copy( subray.linPos0 );
596 segment.linPos1.copy( subray.linPos1 );
597 segment.up0.copy( subray.up0 );
598 segment.up1.copy( subray.up1 );
599 segment.radius0 = subray.radius0;
600 segment.radius1 = subray.radius1;
601 segment.fraction0 = 0;
602 segment.fraction1 = 1;
603 segment.positionVariationFactor = 1 - subray.straightness;
604
605 this.subrayProbability = this.ramification * Math.pow( this.recursionProbability, subray.recursion ) / ( 1 << subray.maxIterations );
606
607 this.fractalRayRecursive( segment );
608
609 }
610
611 this.currentSegmentCallback = null;
612 this.currentSubray = null;
613
614};
615
616THREE.LightningStrike.prototype.fractalRayRecursive = function ( segment ) {
617
618 // Leave recursion condition
619 if ( segment.iteration >= this.currentSubray.maxIterations ) {
620
621 this.currentSegmentCallback( segment );
622
623 return;
624
625 }
626
627 // Interpolation
628 this.forwards.subVectors( segment.pos1, segment.pos0 );
629 var lForwards = this.forwards.length();
630
631 if ( lForwards < 0.000001 ) {
632
633 this.forwards.set( 0, 0, 0.01 );
634 lForwards = this.forwards.length();
635
636 }
637
638 var middleRadius = ( segment.radius0 + segment.radius1 ) * 0.5;
639 var middleFraction = ( segment.fraction0 + segment.fraction1 ) * 0.5;
640
641 var timeDimension = this.time * this.currentSubray.timeScale * Math.pow( 2, segment.iteration );
642
643 this.middlePos.lerpVectors( segment.pos0, segment.pos1, 0.5 );
644 this.middleLinPos.lerpVectors( segment.linPos0, segment.linPos1, 0.5 );
645 var p = this.middleLinPos;
646
647 // Noise
648 this.newPos.set( this.simplexX.noise4d( p.x, p.y, p.z, timeDimension ),
649 this.simplexY.noise4d( p.x, p.y, p.z, timeDimension ),
650 this.simplexZ.noise4d( p.x, p.y, p.z, timeDimension ) );
651
652 this.newPos.multiplyScalar( segment.positionVariationFactor * lForwards );
653 this.newPos.add( this.middlePos );
654
655 // Recursion
656
657 var newSegment1 = this.getNewSegment();
658 newSegment1.pos0.copy( segment.pos0 );
659 newSegment1.pos1.copy( this.newPos );
660 newSegment1.linPos0.copy( segment.linPos0 );
661 newSegment1.linPos1.copy( this.middleLinPos );
662 newSegment1.up0.copy( segment.up0 );
663 newSegment1.up1.copy( segment.up1 );
664 newSegment1.radius0 = segment.radius0;
665 newSegment1.radius1 = middleRadius;
666 newSegment1.fraction0 = segment.fraction0;
667 newSegment1.fraction1 = middleFraction;
668 newSegment1.positionVariationFactor = segment.positionVariationFactor * this.currentSubray.roughness;
669 newSegment1.iteration = segment.iteration + 1;
670
671 var newSegment2 = this.getNewSegment();
672 newSegment2.pos0.copy( this.newPos );
673 newSegment2.pos1.copy( segment.pos1 );
674 newSegment2.linPos0.copy( this.middleLinPos );
675 newSegment2.linPos1.copy( segment.linPos1 );
676 this.cross1.crossVectors( segment.up0, this.forwards.normalize() );
677 newSegment2.up0.crossVectors( this.forwards, this.cross1 ).normalize();
678 newSegment2.up1.copy( segment.up1 );
679 newSegment2.radius0 = middleRadius;
680 newSegment2.radius1 = segment.radius1;
681 newSegment2.fraction0 = middleFraction;
682 newSegment2.fraction1 = segment.fraction1;
683 newSegment2.positionVariationFactor = segment.positionVariationFactor * this.currentSubray.roughness;
684 newSegment2.iteration = segment.iteration + 1;
685
686 this.fractalRayRecursive( newSegment1 );
687
688 this.fractalRayRecursive( newSegment2 );
689
690};
691
692THREE.LightningStrike.prototype.createPrism = function ( segment ) {
693
694 // Creates one triangular prism and its vertices at the segment
695
696 this.forwardsFill.subVectors( segment.pos1, segment.pos0 ).normalize();
697
698 if ( this.isInitialSegment ) {
699
700 this.currentCreateTriangleVertices( segment.pos0, segment.up0, this.forwardsFill, segment.radius0, 0 );
701
702 this.isInitialSegment = false;
703
704 }
705
706 this.currentCreateTriangleVertices( segment.pos1, segment.up0, this.forwardsFill, segment.radius1, segment.fraction1 );
707
708 this.createPrismFaces();
709
710};
711
712THREE.LightningStrike.prototype.createTriangleVerticesWithoutUVs = function ( pos, up, forwards, radius ) {
713
714 // Create an equilateral triangle (only vertices)
715
716 this.side.crossVectors( up, forwards ).multiplyScalar( radius * THREE.LightningStrike.COS30DEG );
717 this.down.copy( up ).multiplyScalar( - radius * THREE.LightningStrike.SIN30DEG );
718
719 var p = this.vPos;
720 var v = this.vertices;
721
722 p.copy( pos ).sub( this.side ).add( this.down );
723
724 v[ this.currentCoordinate ++ ] = p.x;
725 v[ this.currentCoordinate ++ ] = p.y;
726 v[ this.currentCoordinate ++ ] = p.z;
727
728 p.copy( pos ).add( this.side ).add( this.down );
729
730 v[ this.currentCoordinate ++ ] = p.x;
731 v[ this.currentCoordinate ++ ] = p.y;
732 v[ this.currentCoordinate ++ ] = p.z;
733
734 p.copy( up ).multiplyScalar( radius ).add( pos );
735
736 v[ this.currentCoordinate ++ ] = p.x;
737 v[ this.currentCoordinate ++ ] = p.y;
738 v[ this.currentCoordinate ++ ] = p.z;
739
740 this.currentVertex += 3;
741
742};
743
744THREE.LightningStrike.prototype.createTriangleVerticesWithUVs = function ( pos, up, forwards, radius, u ) {
745
746 // Create an equilateral triangle (only vertices)
747
748 this.side.crossVectors( up, forwards ).multiplyScalar( radius * THREE.LightningStrike.COS30DEG );
749 this.down.copy( up ).multiplyScalar( - radius * THREE.LightningStrike.SIN30DEG );
750
751 var p = this.vPos;
752 var v = this.vertices;
753 var uv = this.uvs;
754
755 p.copy( pos ).sub( this.side ).add( this.down );
756
757 v[ this.currentCoordinate ++ ] = p.x;
758 v[ this.currentCoordinate ++ ] = p.y;
759 v[ this.currentCoordinate ++ ] = p.z;
760
761 uv[ this.currentUVCoordinate ++ ] = u;
762 uv[ this.currentUVCoordinate ++ ] = 0;
763
764 p.copy( pos ).add( this.side ).add( this.down );
765
766 v[ this.currentCoordinate ++ ] = p.x;
767 v[ this.currentCoordinate ++ ] = p.y;
768 v[ this.currentCoordinate ++ ] = p.z;
769
770 uv[ this.currentUVCoordinate ++ ] = u;
771 uv[ this.currentUVCoordinate ++ ] = 0.5;
772
773 p.copy( up ).multiplyScalar( radius ).add( pos );
774
775 v[ this.currentCoordinate ++ ] = p.x;
776 v[ this.currentCoordinate ++ ] = p.y;
777 v[ this.currentCoordinate ++ ] = p.z;
778
779 uv[ this.currentUVCoordinate ++ ] = u;
780 uv[ this.currentUVCoordinate ++ ] = 1;
781
782 this.currentVertex += 3;
783
784};
785
786THREE.LightningStrike.prototype.createPrismFaces = function ( vertex/*, index*/ ) {
787
788 var indices = this.indices;
789 var vertex = this.currentVertex - 6;
790
791 indices[ this.currentIndex ++ ] = vertex + 1;
792 indices[ this.currentIndex ++ ] = vertex + 2;
793 indices[ this.currentIndex ++ ] = vertex + 5;
794 indices[ this.currentIndex ++ ] = vertex + 1;
795 indices[ this.currentIndex ++ ] = vertex + 5;
796 indices[ this.currentIndex ++ ] = vertex + 4;
797 indices[ this.currentIndex ++ ] = vertex + 0;
798 indices[ this.currentIndex ++ ] = vertex + 1;
799 indices[ this.currentIndex ++ ] = vertex + 4;
800 indices[ this.currentIndex ++ ] = vertex + 0;
801 indices[ this.currentIndex ++ ] = vertex + 4;
802 indices[ this.currentIndex ++ ] = vertex + 3;
803 indices[ this.currentIndex ++ ] = vertex + 2;
804 indices[ this.currentIndex ++ ] = vertex + 0;
805 indices[ this.currentIndex ++ ] = vertex + 3;
806 indices[ this.currentIndex ++ ] = vertex + 2;
807 indices[ this.currentIndex ++ ] = vertex + 3;
808 indices[ this.currentIndex ++ ] = vertex + 5;
809
810};
811
812THREE.LightningStrike.prototype.createDefaultSubrayCreationCallbacks = function () {
813
814 var random1 = this.randomGenerator.random;
815
816 this.onDecideSubrayCreation = function ( segment, lightningStrike ) {
817
818 // Decide subrays creation at parent (sub)ray segment
819
820 var subray = lightningStrike.currentSubray;
821
822 var period = lightningStrike.rayParameters.subrayPeriod;
823 var dutyCycle = lightningStrike.rayParameters.subrayDutyCycle;
824
825 var phase0 = ( lightningStrike.rayParameters.isEternal && subray.recursion == 0 ) ? - random1() * period : THREE.MathUtils.lerp( subray.birthTime, subray.endPropagationTime, segment.fraction0 ) - random1() * period;
826
827 var phase = lightningStrike.time - phase0;
828 var currentCycle = Math.floor( phase / period );
829
830 var childSubraySeed = random1() * ( currentCycle + 1 );
831
832 var isActive = phase % period <= dutyCycle * period;
833
834 var probability = 0;
835
836 if ( isActive ) {
837
838 probability = lightningStrike.subrayProbability;
839 // Distribution test: probability *= segment.fraction0 > 0.5 && segment.fraction0 < 0.9 ? 1 / 0.4 : 0;
840
841 }
842
843 if ( subray.recursion < lightningStrike.maxSubrayRecursion && lightningStrike.numSubrays < lightningStrike.maxSubrays && random1() < probability ) {
844
845 var childSubray = lightningStrike.addNewSubray();
846
847 var parentSeed = lightningStrike.randomGenerator.getSeed();
848 childSubray.seed = childSubraySeed;
849 lightningStrike.randomGenerator.setSeed( childSubraySeed );
850
851 childSubray.recursion = subray.recursion + 1;
852 childSubray.maxIterations = Math.max( 1, subray.maxIterations - 1 );
853
854 childSubray.linPos0.set( random1(), random1(), random1() ).multiplyScalar( 1000 );
855 childSubray.linPos1.set( random1(), random1(), random1() ).multiplyScalar( 1000 );
856 childSubray.up0.copy( subray.up0 );
857 childSubray.up1.copy( subray.up1 );
858 childSubray.radius0 = segment.radius0 * lightningStrike.rayParameters.radius0Factor;
859 childSubray.radius1 = Math.min( lightningStrike.rayParameters.minRadius, segment.radius1 * lightningStrike.rayParameters.radius1Factor );
860
861 childSubray.birthTime = phase0 + ( currentCycle ) * period;
862 childSubray.deathTime = childSubray.birthTime + period * dutyCycle;
863
864 if ( ! lightningStrike.rayParameters.isEternal && subray.recursion == 0 ) {
865
866 childSubray.birthTime = Math.max( childSubray.birthTime, subray.birthTime );
867 childSubray.deathTime = Math.min( childSubray.deathTime, subray.deathTime );
868
869 }
870
871 childSubray.timeScale = subray.timeScale * 2;
872 childSubray.roughness = subray.roughness;
873 childSubray.straightness = subray.straightness;
874 childSubray.propagationTimeFactor = subray.propagationTimeFactor;
875 childSubray.vanishingTimeFactor = subray.vanishingTimeFactor;
876
877 lightningStrike.onSubrayCreation( segment, subray, childSubray, lightningStrike );
878
879 lightningStrike.randomGenerator.setSeed( parentSeed );
880
881 }
882
883 };
884
885 var vec1Pos = new THREE.Vector3();
886 var vec2Forward = new THREE.Vector3();
887 var vec3Side = new THREE.Vector3();
888 var vec4Up = new THREE.Vector3();
889
890 this.onSubrayCreation = function ( segment, parentSubray, childSubray, lightningStrike ) {
891
892 // Decide childSubray origin and destination positions (pos0 and pos1) and possibly other properties of childSubray
893
894 // Just use the default cone position generator
895 lightningStrike.subrayCylinderPosition( segment, parentSubray, childSubray, 0.5, 0.6, 0.2 );
896
897 };
898
899 this.subrayConePosition = function ( segment, parentSubray, childSubray, heightFactor, sideWidthFactor, minSideWidthFactor ) {
900
901 // Sets childSubray pos0 and pos1 in a cone
902
903 childSubray.pos0.copy( segment.pos0 );
904
905 vec1Pos.subVectors( parentSubray.pos1, parentSubray.pos0 );
906 vec2Forward.copy( vec1Pos ).normalize();
907 vec1Pos.multiplyScalar( segment.fraction0 + ( 1 - segment.fraction0 ) * ( random1() * heightFactor ) );
908 var length = vec1Pos.length();
909 vec3Side.crossVectors( parentSubray.up0, vec2Forward );
910 var angle = 2 * Math.PI * random1();
911 vec3Side.multiplyScalar( Math.cos( angle ) );
912 vec4Up.copy( parentSubray.up0 ).multiplyScalar( Math.sin( angle ) );
913
914 childSubray.pos1.copy( vec3Side ).add( vec4Up ).multiplyScalar( length * sideWidthFactor * ( minSideWidthFactor + random1() * ( 1 - minSideWidthFactor ) ) ).add( vec1Pos ).add( parentSubray.pos0 );
915
916 };
917
918 this.subrayCylinderPosition = function ( segment, parentSubray, childSubray, heightFactor, sideWidthFactor, minSideWidthFactor ) {
919
920 // Sets childSubray pos0 and pos1 in a cylinder
921
922 childSubray.pos0.copy( segment.pos0 );
923
924 vec1Pos.subVectors( parentSubray.pos1, parentSubray.pos0 );
925 vec2Forward.copy( vec1Pos ).normalize();
926 vec1Pos.multiplyScalar( segment.fraction0 + ( 1 - segment.fraction0 ) * ( ( 2 * random1() - 1 ) * heightFactor ) );
927 var length = vec1Pos.length();
928 vec3Side.crossVectors( parentSubray.up0, vec2Forward );
929 var angle = 2 * Math.PI * random1();
930 vec3Side.multiplyScalar( Math.cos( angle ) );
931 vec4Up.copy( parentSubray.up0 ).multiplyScalar( Math.sin( angle ) );
932
933 childSubray.pos1.copy( vec3Side ).add( vec4Up ).multiplyScalar( length * sideWidthFactor * ( minSideWidthFactor + random1() * ( 1 - minSideWidthFactor ) ) ).add( vec1Pos ).add( parentSubray.pos0 );
934
935 };
936
937};
938
939THREE.LightningStrike.prototype.createSubray = function () {
940
941 return {
942
943 seed: 0,
944 maxIterations: 0,
945 recursion: 0,
946 pos0: new THREE.Vector3(),
947 pos1: new THREE.Vector3(),
948 linPos0: new THREE.Vector3(),
949 linPos1: new THREE.Vector3(),
950 up0: new THREE.Vector3(),
951 up1: new THREE.Vector3(),
952 radius0: 0,
953 radius1: 0,
954 birthTime: 0,
955 deathTime: 0,
956 timeScale: 0,
957 roughness: 0,
958 straightness: 0,
959 propagationTimeFactor: 0,
960 vanishingTimeFactor: 0,
961 endPropagationTime: 0,
962 beginVanishingTime: 0
963
964 };
965
966};
967
968THREE.LightningStrike.prototype.createSegment = function () {
969
970 return {
971 iteration: 0,
972 pos0: new THREE.Vector3(),
973 pos1: new THREE.Vector3(),
974 linPos0: new THREE.Vector3(),
975 linPos1: new THREE.Vector3(),
976 up0: new THREE.Vector3(),
977 up1: new THREE.Vector3(),
978 radius0: 0,
979 radius1: 0,
980 fraction0: 0,
981 fraction1: 0,
982 positionVariationFactor: 0
983 };
984
985};
986
987THREE.LightningStrike.prototype.getNewSegment = function () {
988
989 return this.raySegments[ this.currentSegmentIndex ++ ];
990
991};
992
993THREE.LightningStrike.prototype.copy = function ( source ) {
994
995 THREE.BufferGeometry.prototype.copy.call( this, source );
996
997 this.init( THREE.LightningStrike.copyParameters( {}, source.rayParameters ) );
998
999 return this;
1000
1001};
1002
1003THREE.LightningStrike.prototype.clone = function () {
1004
1005 return new this.constructor( THREE.LightningStrike.copyParameters( {}, this.rayParameters ) );
1006
1007};