Richard Boegli's CnC_Generals_Zero_Hour Fork WIP
This is documentation of Richard Boegil's Zero Hour Fork
 
Loading...
Searching...
No Matches
part_buf.cpp
Go to the documentation of this file.
1/*
2** Command & Conquer Generals Zero Hour(tm)
3** Copyright 2025 Electronic Arts Inc.
4**
5** This program is free software: you can redistribute it and/or modify
6** it under the terms of the GNU General Public License as published by
7** the Free Software Foundation, either version 3 of the License, or
8** (at your option) any later version.
9**
10** This program is distributed in the hope that it will be useful,
11** but WITHOUT ANY WARRANTY; without even the implied warranty of
12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13** GNU General Public License for more details.
14**
15** You should have received a copy of the GNU General Public License
16** along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/***************************************************************************
20 *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
21 ***************************************************************************
22 * *
23 * Project Name : G *
24 * *
25 * $Archive:: /Commando/Code/ww3d2/part_buf.cpp $*
26 * *
27 * $Author:: Jani_p $*
28 * *
29 * $Modtime:: 9/07/01 12:57p $*
30 * *
31 * $Revision:: 20 $*
32 * *
33 *-------------------------------------------------------------------------*
34 * Functions: *
35 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
36#include "part_buf.h"
37#include "part_emt.h"
38#include "ww3d.h"
39#include "rinfo.h"
40#include "scene.h"
41#include "camera.h"
42#include "predlod.h"
43#include "pot.h"
44#include "bound.h"
45#include "simplevec.h"
46#include "sphere.h"
47#include "wwprofile.h"
48#include <limits.h>
49#include "vp.h"
50#include "texture.h"
51#include "dx8wrapper.h"
52#include "vector3.h"
53
54// A random permutation of the numbers 0 to 15 - used for LOD particle decimation.
55// It was generated by the amazingly high-tech method of pulling numbers out of a hat.
56const unsigned int ParticleBufferClass::PermutationArray[16] = {
57 11, 3, 7, 14, 0, 13, 1, 2, 5, 12, 15, 6, 9, 8, 4, 10
58};
59
60// Maximum size of randomizer tables
61const static unsigned int MAX_RANDOM_ENTRIES = 32; // MUST be power of two!
62
63// Total Active Particle Buffer Count
65
66// Static array of screen-size clamps for the 17 possible LOD levels a particle buffer can have.
67// We can change these from being global to being per-buffer later if we wish. Default is
68// NO_MAX_SCREEN_SIZE.
75};
76
77static Random4Class rand_gen;
78const float oo_intmax = 1.0f / (float)INT_MAX;
79
80// Default Line Emitter Properties
81static const W3dEmitterLinePropertiesStruct _DefaultLineEmitterProps=
82{ 0,0,0.0f,1.5f,1.0f,0.0f,0.0f,0,0,0,0,0,0,0,0,0 };
83
85(
86 ParticleEmitterClass *emitter,
87 unsigned int buffer_size,
92 float orient_rnd,
95 Vector3 accel,
96 float max_age,
97 float future_start,
98 TextureClass *tex,
99 ShaderClass shader,
100 bool pingpong,
101 int render_mode,
102 int frame_mode,
103 const W3dEmitterLinePropertiesStruct * line_props
104) :
109 RenderMode(render_mode),
110 FrameMode(frame_mode),
111 MaxAge(1000.0f * max_age),
112 FutureStartTime(1000.0f * future_start),
113 LastUpdateTime(WW3D::Get_Sync_Time()),
115 MaxSize(0.0f),
116 MaxNum(buffer_size),
117 Start(0U),
118 End(0U),
119 NewEnd(0U),
120 NonNewNum(0),
121 NewNum(0),
122 BoundingBox(Vector3(0,0,0),Vector3(0,0,0)),
155 ColorRandom(0, 0, 0),
156 OpacityRandom(0),
157 SizeRandom(0),
159 FrameRandom(0),
172 Diffuse(NULL),
174 Color(NULL),
175 Alpha(NULL),
176 Size(NULL),
178 Frame(NULL),
179 UCoord(NULL),
181 APT(NULL),
182 GroupID(NULL),
183 PingPongPosition(pingpong),
184 Velocity(NULL),
186 Emitter(emitter),
188 ProjectedArea(0.0f),
189 DefaultTailDiffuse(0,0,0,0),
191{
192 LodCount = 17;
193 LodBias = 1.0f;
194
195 Position[0] = NULL;
196 Position[1] = NULL;
197
198 // Create color array, keyframes and randomizer table (if needed)
199 Reset_Colors(color);
200
201 // Create alpha array, keyframes and randomizer table (if needed)
202 Reset_Opacity(opacity);
203
204 // Create size array, keyframes and randomizer table (if needed)
205 Reset_Size(size);
206
207 // Create the rotation array, keyframes, and randomizer table (if needed)
208 Reset_Rotations(rotation, orient_rnd);
209
210 // Create the frame array, keyframes, and randomizer table (if needed)
211 Reset_Frames(frame);
212
213 // Create the blur time array, keyframes, and randomizer table if needed
214 Reset_Blur_Times(blurtime);
215
216 // We do not add a ref for the emitter (see DTor for detailed explanation)
217 // if (Emitter) Emitter->Add_Ref();
218
219 // Set up new particle queue:
221
222 // These inputs don't need to be range-checked (emitter did that).
223 Accel = accel;
224 HasAccel = (accel.X != 0.0f) || (accel.Y != 0.0f) || (accel.Z != 0.0f);
225
226 shader.Enable_Fog ("ParticleBufferClass");
227 switch (RenderMode)
228 {
230 {
231 // Set up worldspace point group
233 PointGroup->Set_Flag(PointGroupClass::TRANSFORM, true);
234 PointGroup->Set_Texture(tex);
235 PointGroup->Set_Shader(shader);
236 PointGroup->Set_Frame_Row_Column_Count_Log2(frame_mode);
237 PointGroup->Set_Point_Mode(PointGroupClass::TRIS);
238 }
239 break;
241 {
242 // Set up worldspace point group
244 PointGroup->Set_Flag(PointGroupClass::TRANSFORM, true);
245 PointGroup->Set_Texture(tex);
246 PointGroup->Set_Shader(shader);
247 PointGroup->Set_Frame_Row_Column_Count_Log2(frame_mode);
248 PointGroup->Set_Point_Mode(PointGroupClass::QUADS);
249 }
250 break;
252 {
254 LineRenderer->Init(*line_props);
255 LineRenderer->Set_Texture(tex);
256 LineRenderer->Set_Shader(shader);
257 LineRenderer->Set_Width(Get_Particle_Size());
258 if (line_props != NULL) {
259 LineRenderer->Init(*line_props);
260 } else {
261 // This code should not be run, but if it does,
262 // set line emitters to some reasonable value so
263 // it doesn't crash
264 WWASSERT(0);
265 LineRenderer->Init(_DefaultLineEmitterProps);
266 }
267 }
268 break;
270 {
272 LineGroup->Set_Flag(LineGroupClass::TRANSFORM, true);
273 LineGroup->Set_Texture(tex);
274 LineGroup->Set_Shader(shader);
276 TailPosition = NEW_REF( ShareBufferClass<Vector3> , (MaxNum, "ParticleBufferClass::TailPosition" ) );
277 // TODO: Change TailPosition to Kinematic state and add
278 // tail positions to bounding box
280 }
281 break;
283 {
285 LineGroup->Set_Flag(LineGroupClass::TRANSFORM, true);
286 LineGroup->Set_Texture(tex);
287 LineGroup->Set_Shader(shader);
288 LineGroup->Set_Line_Mode(LineGroupClass::PRISM);
289 TailPosition = NEW_REF( ShareBufferClass<Vector3> , (MaxNum, "ParticleBufferClass::TailPosition" ) );
290 // TODO: Change TailPosition to Kinematic state and add
291 // tail positions to bounding box
293 }
294 break;
295 default:
296 WWASSERT(0);
297 break;
298 }
299
300 // Set up circular buffer. Contents are not initialized because the
301 // start/end indices currently indicate the buffer is empty.
302 Position[0] = NEW_REF( ShareBufferClass<Vector3> , (MaxNum, "ParticleBufferClass::Position") );
303 if (PingPongPosition) {
304 Position[1] = NEW_REF( ShareBufferClass<Vector3> , (MaxNum, "ParticleBufferClass::Position") );
305 }
306 APT = NEW_REF( ShareBufferClass<unsigned int> , (MaxNum, "ParticleBufferClass::APT") );
307 GroupID = NEW_REF( ShareBufferClass<unsigned char> , (MaxNum, "ParticleBufferClass::GroupID") );
309 TimeStamp = W3DNEWARRAY unsigned int[MaxNum];
310
311 // So that the object is ready for use after construction, we will
312 // complete its initialization by initializing its cost and value arrays
313 // according to a screen area of 1.
314 int minlod = Calculate_Cost_Value_Arrays(1.0f, Value, Cost);
315
316 // Ensure lod is no less than minimum allowed
317 if (Get_LOD_Level() < minlod) Set_LOD_Level(minlod);
318
319 // Update Global Count
321
322 //lorenzen
323 // If the render mode is W3D_EMITTER_RENDER_MODE_LINE and we are supplied with
324 // a line properties structure, set up a line renderer
326 if (line_props != NULL) {
328 LineRenderer->Init(*line_props);
329 LineRenderer->Set_Texture(tex);
330 LineRenderer->Set_Shader(shader);
331 LineRenderer->Set_Width(Get_Particle_Size());
332 } else {
333 // We were in line mode but didn't get any line properties, drop back to triangles
335 }
336 }
337}
338
339
341 RenderObjClass(src),
347 FrameMode(src.FrameMode),
348 MaxAge(src.MaxAge),
350 LastUpdateTime(WW3D::Get_Sync_Time()),
352 MaxSize(src.MaxSize),
353 MaxNum(src.MaxNum),
354 Start(0U),
355 End(0U),
356 NewEnd(0U),
357 NonNewNum(0),
358 NewNum(0),
359 BoundingBox(Vector3(0,0,0),Vector3(0,0,0)),
406 Diffuse(NULL),
408 Color(NULL),
409 Alpha(NULL),
410 Size(NULL),
412 Frame(NULL),
413 UCoord(NULL),
415 APT(NULL),
416 GroupID(NULL),
418 Velocity(NULL),
420 Emitter(src.Emitter),
422 ProjectedArea(0.0f),
424{
425 Position[0] = NULL;
426 Position[1] = NULL;
427
428 unsigned int i;
429
430 LodCount = MIN(MaxNum, 17);
431 LodBias = src.LodBias;
432
433 /*
434 ** Create visual state arrays, copy keyframes and randomizer tables.
435 */
436
438 if (src.Color) {
439 // Create color array
440 Color = NEW_REF( ShareBufferClass<Vector3> , (MaxNum, "ParticleBufferClass::Color") );
441
442 // Copy color keyframes
446 for (i = 0; i < NumColorKeyFrames; i++) {
450 }
451
452 // Copy color randomizer table
453 if (src.RandomColorEntries) {
455 for (unsigned int j = 0; j <= NumRandomColorEntriesMinus1; j++) {
457 }
458 }
459 } else {
462 }
463
465 if (src.Alpha) {
466 // Create alpha array
467 Alpha = NEW_REF( ShareBufferClass<float> , (MaxNum, "ParticleBufferClass::Alpha") );
468
469 // Copy alpha keyframes
473 for (i = 0; i < NumAlphaKeyFrames; i++) {
477 }
478
479 // Copy alpha randomizer table
480 if (src.RandomAlphaEntries) {
482 for (unsigned int j = 0; j <= NumRandomAlphaEntriesMinus1; j++) {
484 }
485 }
486 } else {
489 }
490
492 if (src.Size) {
493 // Create size array
494 Size = NEW_REF( ShareBufferClass<float> , (MaxNum, "ParticleBufferClass::Size") );
495
496 // Copy size keyframes
500 for (i = 0; i < NumSizeKeyFrames; i++) {
504 }
505
506 // Copy size randomizer table
507 if (src.RandomSizeEntries) {
509 for (unsigned int j = 0; j <= NumRandomSizeEntriesMinus1; j++) {
511 }
512 }
513 } else {
516 }
517
518 // Set up the rotation / orientation keyframes
521 if (src.Orientation) {
522 // Create orientation array
523 Orientation = NEW_REF( ShareBufferClass<uint8> , (MaxNum, "ParticleBufferClass::Orientation") );
524
525 // Copy rotation / orientation keyframes
530 for (i = 0; i < NumRotationKeyFrames; i++) {
535 }
536
537 // Copy rotation randomizer table
538 if (src.RandomRotationEntries) {
540 for (unsigned int j = 0; j <= NumRandomRotationEntriesMinus1; j++) {
542 }
543 }
544
545 // Copy starting orientation randomizer table
546 if (src.RandomOrientationEntries) {
548 for (unsigned int j = 0; j <= NumRandomOrientationEntriesMinus1; j++) {
550 }
551 }
552
553 } else {
554 // Unlike other properties, if there is no Orientation array then all the arrays are NULL
555 // (including the Values array) - there is an implicit starting value of 0.
556 }
557
558
559 // Set up the frame keyframes
560 // Frame and UCoord both use Frame Key Frames for the source data
562 if (src.Frame || src.UCoord) {
563 // Create frame array
564 if (src.Frame) {
565 Frame = NEW_REF( ShareBufferClass<uint8> , (MaxNum, "ParticleBufferClass::Frame") );
566 } else {
567 UCoord = NEW_REF( ShareBufferClass<float>, (MaxNum, "ParticleBufferClass::UCoord") );
568 }
569
570 // Copy frame keyframes
574 for (i = 0; i < NumFrameKeyFrames; i++) {
578 }
579
580 // Copy frame randomizer table
581 if (src.RandomFrameEntries) {
583 for (unsigned int j = 0; j <= NumRandomFrameEntriesMinus1; j++) {
585 }
586 }
587 } else {
590 }
591
592 // Set up the blur times keyframes
594 if (NumBlurTimeKeyFrames > 0) {
595 // Copy blur time keyframes
596 BlurTimeKeyFrameTimes = new unsigned int [NumBlurTimeKeyFrames];
599 for (i = 0; i < NumBlurTimeKeyFrames; i++) {
603 }
604
605 // Copy blur time randomizer table
606 if (src.RandomBlurTimeEntries) {
608 for (unsigned int j = 0; j <= NumRandomBlurTimeEntriesMinus1; j++) {
610 }
611 }
612 } else {
613 BlurTimeKeyFrameValues = new float [1];
615 }
616
617
618 // We do not add a ref for the emitter (see DTor for detailed explanation)
619 // if (Emitter) Emitter->Add_Ref();
620
621 // Set up new particle queue:
623
624 // Inputs don't need to be range-checked (emitter did that).
625 Accel = src.Accel;
626 HasAccel = src.HasAccel;
627
628 switch (RenderMode)
629 {
631 {
632 // Set up worldspace point group
633 WWASSERT(src.PointGroup);
635 PointGroup->Set_Flag(PointGroupClass::TRANSFORM, true);
636 PointGroup->Set_Texture(src.PointGroup->Peek_Texture());
637 PointGroup->Set_Shader(src.PointGroup->Get_Shader());
638 PointGroup->Set_Point_Mode(PointGroupClass::TRIS);
639 PointGroup->Set_Frame_Row_Column_Count_Log2(src.PointGroup->Get_Frame_Row_Column_Count_Log2());
640 }
641 break;
643 {
644 // Set up worldspace point group
645 WWASSERT(src.PointGroup);
647 PointGroup->Set_Flag(PointGroupClass::TRANSFORM, true);
648 PointGroup->Set_Texture(src.PointGroup->Peek_Texture());
649 PointGroup->Set_Shader(src.PointGroup->Get_Shader());
650 PointGroup->Set_Point_Mode(PointGroupClass::QUADS);
651 PointGroup->Set_Frame_Row_Column_Count_Log2(src.PointGroup->Get_Frame_Row_Column_Count_Log2());
652 }
653 break;
655 {
658 }
659 break;
661 {
662 WWASSERT(src.LineGroup);
664 LineGroup->Set_Flag(LineGroupClass::TRANSFORM, true);
665 LineGroup->Set_Texture(src.LineGroup->Peek_Texture());
666 LineGroup->Set_Shader(src.LineGroup->Get_Shader());
668 TailPosition = NEW_REF( ShareBufferClass<Vector3> , (MaxNum, "ParticleBufferClass::TailPosition") );
669 // TODO: Change TailPosition to Kinematic state and add
670 // tail positions to bounding box
672 }
673 break;
675 {
676 WWASSERT(src.LineGroup);
678 LineGroup->Set_Flag(LineGroupClass::TRANSFORM, true);
679 LineGroup->Set_Texture(src.LineGroup->Peek_Texture());
680 LineGroup->Set_Shader(src.LineGroup->Get_Shader());
681 LineGroup->Set_Line_Mode(LineGroupClass::PRISM);
682 TailPosition = NEW_REF( ShareBufferClass<Vector3> , (MaxNum, "ParticleBufferClass::TailPosition") );
683 // TODO: Change TailPosition to Kinematic state and add
684 // tail positions to bounding box
686 }
687 break;
688 default:
689 WWASSERT(0);
690 break;
691 }
692
693 // Set up circular buffer. Contents are not initialized because the
694 // start/end indices currently indicate the buffer is empty.
695 Position[0] = NEW_REF( ShareBufferClass<Vector3> , (MaxNum, "ParticleBufferClass::Position") );
696 if (PingPongPosition) {
697 Position[1] = NEW_REF( ShareBufferClass<Vector3> , (MaxNum, "ParticleBufferClass::Position") );
698 }
699 APT = NEW_REF( ShareBufferClass<unsigned int> , (MaxNum, "ParticleBufferClass::APT") );
700 GroupID = NEW_REF( ShareBufferClass<unsigned char> , (MaxNum, "ParticleBufferClass::GroupID") );
702 TimeStamp = W3DNEWARRAY unsigned int[MaxNum];
703
704 // So that the object is ready for use after construction, we will
705 // complete its initialization by initializing its cost and value arrays
706 // according to a screen area of 1.
707 int minlod = Calculate_Cost_Value_Arrays(1.0f, Value, Cost);
708
709 // Ensure lod is no less than minimum allowed
710 if (Get_LOD_Level() < minlod) Set_LOD_Level(minlod);
711
712 // Update Global Count
714}
715
716
718{
720
721 if (this != &that) {
722 assert(0); // TODO: if you hit this assert, please implement me !!!;-)
723 }
724
725 return * this;
726}
727
728
730{
731 if (NewParticleQueue) delete [] NewParticleQueue;
758
759 if (PointGroup) delete PointGroup;
760 if (LineRenderer) delete LineRenderer;
761 if (LineGroup) delete LineGroup;
762
776
777 if (Velocity) delete [] Velocity;
778 if (TimeStamp) delete [] TimeStamp;
779 if (Emitter) {
780 // We should not have an emitter at this point, since the emitter
781 // should still have a live ref to us if it still exists which would
782 // prevent us from getting killed.
783 assert(0);
784 // We do not release-ref the emitter pointer because we did not add a
785 // ref for it to begin with; the ref is not needed (if the emitter gets
786 // deleted it will tell us to clear our emitter pointer) and actually
787 // harmful (if emitter and buffer each have refcounted pointers to the
788 // other neither would ever get deleted).
789 // Emitter->Release_Ref();
790 Emitter = NULL;
791 }
792
793 // Update Global Count
795}
796
797
799{
800 return W3DNEW ParticleBufferClass(*this);
801}
802
803
805{
806 // Currently in particle buffers, the cost happens to be equal to thwe polygon count.
807 return (int)Get_Cost();
808}
809
811{
812 return NonNewNum + NewNum;
813}
814
816{
817 WWPROFILE("ParticleBuffer::Render");
818
819 unsigned int sort_level = SORT_LEVEL_NONE;
820
822 sort_level=Get_Shader().Guess_Sort_Level();
823
825
826 WW3D::Add_To_Static_Sort_List(this, sort_level);
827
828 } else {
829 // Ensure particles' kinematic state is updated
831
832 // Since we are rendering the particles, visual state needs to be updated (but not if the
833 // entire particle buffer is decimated away)
834 if (DecimationThreshold < LodCount - 1) {
836 }
837
838 switch( RenderMode )
839 {
842 Render_Particles(rinfo);
843 break;
845 Render_Line(rinfo);
846 break;
849 Render_Line_Group(rinfo);
850 break;
851
852 }
853 }
854}
855
856void ParticleBufferClass::Generate_APT(ShareBufferClass <unsigned int> **apt,unsigned int &active_point_count)
857{
858 if (NonNewNum < (int)MaxNum || DecimationThreshold > 0) {
859 // In the general case, a range in a circular buffer can be composed of up
860 // to two subranges. Find the Start - End subranges.
861 // This differs from other similar code segments because we want to access
862 // the subranges in memory order (rather than in queue order) this time.
863 unsigned int sub1_start; // Start of subrange 1.
864 unsigned int sub1_end; // End of subrange 1.
865 unsigned int sub2_start; // Start of subrange 2.
866 unsigned int sub2_end; // End of subrange 2.
867 unsigned int i; // Loop index.
868 if ((Start < End) || ((Start == End) && NonNewNum == 0)) {
869 sub1_start = Start;
870 sub1_end = End;
871 sub2_start = End;
872 sub2_end = End;
873 } else {
874 sub1_start = 0;
875 sub1_end = End;
876 sub2_start = Start;
877 sub2_end = MaxNum;
878 }
879 // Generate APT:
880 unsigned int *apt_ptr = APT->Get_Array();
881 for (i = sub1_start; i < sub1_end; i++) {
882 if (PermutationArray[i & 0xF] >= DecimationThreshold) {
883 apt_ptr[active_point_count++] = i;
884 }
885 }
886 for (i = sub2_start; i < sub2_end; i++) {
887 if (PermutationArray[i & 0xF] >= DecimationThreshold) {
888 apt_ptr[active_point_count++] = i;
889 }
890 }
891 *apt = APT;
892 } else {
893 active_point_count = NonNewNum;
894 }
895}
896
898{
899 // Temporary array copying to combine diffuse and alpha to one array.
900 if (Color || Alpha) {
901 unsigned cnt=MaxNum;
902 if (!Diffuse) {
903 Diffuse = NEW_REF( ShareBufferClass<Vector4> , (MaxNum, "ParticleBufferClass::Diffuse") );
904 }
905 if (Color && Alpha) {
907 Diffuse->Get_Array(),
908 Color->Get_Array(),
909 Alpha->Get_Array(),
910 cnt);
911 }
912 else if (Color) {
914 Diffuse->Get_Array(),
915 Color->Get_Array(),
916 1.0f,
917 cnt);
918 }
919 else {
921 Diffuse->Get_Array(),
922 Vector3(1.0f,1.0f,1.0f),
923 Alpha->Get_Array(),
924 cnt);
925 }
927 Diffuse->Get_Array(),
928 Diffuse->Get_Array(),
929 0.0f,
930 1.0f,
931 cnt);
932 }
933 else if (Diffuse) {
934 Diffuse->Release_Ref();
936 }
937}
938
940{
941 // If the number of active points is less than the maximum or we need to decimate particles
942 // (for LOD purposes), build the active point table:
944
945 unsigned int active_point_count = 0;
946
947 Generate_APT(&apt,active_point_count);
948
949 // Set color, alpha, size defaults if array not present:
950 if (!Color) {
951 PointGroup->Set_Point_Color(ColorKeyFrameValues[0]);
952 }
953 if (!Alpha) {
954 PointGroup->Set_Point_Alpha(AlphaKeyFrameValues[0]);
955 }
956 if (!Size) {
957 PointGroup->Set_Point_Size(SizeKeyFrameValues[0]);
958 }
959 if (!Orientation) {
960 // The rotation keyframes are used to derive the orientation indirectly, as well as the
961 // starting orientation randomizer. If there is no Orientation array that means both are
962 // absent so the orientation should just be set to 0.
963 PointGroup->Set_Point_Orientation(0);
964 }
965 if (!Frame) {
966 PointGroup->Set_Point_Frame(((int)(FrameKeyFrameValues[0])) & 0xFF);
967 }
968
969
970 // Pass the point buffer to the point group and render it.
971 // If we are using pingpong position buffers pass the right one
972 int pingpong = 0;
973 if (PingPongPosition) {
974 pingpong = WW3D::Get_Frame_Count() & 0x1;
975 }
976
978
979 PointGroup->Set_Arrays(Position[pingpong], Diffuse, apt, Size, Orientation, Frame, active_point_count);
981 PointGroup->Render(rinfo);
982}
983
984
986{
987
988 LineRenderer->Set_Freeze_Random(Is_Freeze_Random());
989
990 // Look up the array to use
991 int pingpong = 0;
992 if (PingPongPosition) {
993 pingpong = WW3D::Get_Frame_Count() & 0x1;
994 }
995
996 // Unroll the circular buffer while skipping LOD'd particles
997 static SimpleDynVecClass<Vector3> tmp_points;
998 static SimpleDynVecClass<Vector4> tmp_diffuse;
1000
1001 Vector3 * positions = Position[pingpong]->Get_Array();
1002 Vector4 * diffuse = 0;
1003 Vector4 default_diffuse(0, 0, 0, 0);
1004 unsigned char *ids = GroupID->Get_Array();
1006 if (Diffuse) {
1007 diffuse = Diffuse->Get_Array();
1008 } else {
1009 default_diffuse.Set(ColorKeyFrameValues[0].X, ColorKeyFrameValues[0].Y, ColorKeyFrameValues[0].Z,
1011 }
1012
1013 unsigned int sub1_end; // End of subrange 1.
1014 unsigned int sub2_start; // Start of subrange 2.
1015 unsigned int i; // Loop index.
1016
1017 if ((Start < End) || ((Start == End) && NonNewNum ==0)) {
1018 sub1_end = End;
1019 sub2_start = End;
1020 } else {
1021 sub1_end = MaxNum;
1022 sub2_start = 0;
1023 }
1024
1025 tmp_points.Delete_All(false);
1026 tmp_diffuse.Delete_All(false);
1027 tmp_id.Delete_All(false);
1028
1029 Vector4 *last_color = &default_diffuse;
1030 unsigned char last_id = 0;
1031
1032 for (i = Start; i < sub1_end; i++) {
1033 if (PermutationArray[i & 0xF] >= DecimationThreshold) {
1034 tmp_points.Add(positions[i]);
1035 last_color = diffuse ? &diffuse[i] : &default_diffuse;
1036 tmp_diffuse.Add(*last_color);
1037 last_id = ids[i];
1038 tmp_id.Add(last_id);
1039 }
1040 }
1041
1042 for (i = sub2_start; i < End; i++) {
1043 if (PermutationArray[i & 0xF] >= DecimationThreshold) {
1044 tmp_points.Add(positions[i]);
1045 last_color = diffuse ? &diffuse[i] : &default_diffuse;
1046 tmp_diffuse.Add(*last_color);
1047 last_id = ids[i];
1048 tmp_id.Add(last_id);
1049 }
1050 }
1051
1052 // add in the emitter's position too for the source
1053 if (Emitter && !Emitter->Is_Stopped() && (last_id == CurrentGroupID)) {
1054 tmp_points.Add(Emitter->Get_Position());
1055 // it has the color of the last point
1056 tmp_diffuse.Add(*last_color);
1057 tmp_id.Add(last_id);
1058 }
1059
1060 // If we got any points, render them
1061 if (tmp_points.Count() > 0) {
1062 SphereClass bounding_sphere;
1063 Get_Obj_Space_Bounding_Sphere(bounding_sphere);
1064
1065 // Draw line segments only if they are in the same group
1066 int count = tmp_points.Count();
1067 int start = 0;
1068 int end = 0;
1069
1070 while (end < count) {
1071 // detect contiguous runs of IDs
1072 while ( (end < count) && (tmp_id[start] == tmp_id[end])) {
1073 end++;
1074 }
1075
1076 // render from start, excluding end
1077 if (end - start > 1) {
1078 LineRenderer->Render(rinfo,
1079 Transform,
1080 end - start,
1081 &(tmp_points[start]),
1082 bounding_sphere,
1083 &(tmp_diffuse[start]));
1084 }
1085 start = end;
1086 }
1087 }
1088}
1089
1091{
1092 // If the number of active points is less than the maximum or we need to decimate particles
1093 // (for LOD purposes), build the active point table:
1095
1096 unsigned int active_point_count = 0;
1097
1098 Generate_APT(&apt,active_point_count);
1099
1100 // Set color, alpha, size defaults if array not present:
1101 if (!Color) {
1102 LineGroup->Set_Line_Color(ColorKeyFrameValues[0]);
1103 }
1104 if (!Alpha) {
1105 LineGroup->Set_Line_Alpha(AlphaKeyFrameValues[0]);
1106 }
1107 if (!Size) {
1108 LineGroup->Set_Line_Size(SizeKeyFrameValues[0]);
1109 }
1110 if (!Frame) {
1111 LineGroup->Set_Line_UCoord(FrameKeyFrameValues[0]);
1112 }
1113
1114
1115 // Pass the point buffer to the line group and render it.
1116 // If we are using pingpong position buffers pass the right one
1117 int pingpong = 0;
1118 if (PingPongPosition) {
1119 pingpong = WW3D::Get_Frame_Count() & 0x1;
1120 }
1121
1123
1125
1126 switch (tailtype)
1127 {
1128 case BLACK:
1130 DefaultTailDiffuse.Set(0,0,0,0);
1131 break;
1132 case WHITE:
1134 DefaultTailDiffuse.Set(1,1,1,1);
1135 break;
1137 // if head is all one color, set tail the same way
1138 if (!Diffuse) {
1141 } else {
1142 // otherwise allocate and copy tail diffuse
1143 if (!TailDiffuse) TailDiffuse=NEW_REF(ShareBufferClass<Vector4>,(MaxNum, "ParticleBufferClass::TailDiffuse"));
1144 for (unsigned int i=0; i<MaxNum; i++) {
1145 Vector4 elt=Diffuse->Get_Element(i);
1146 elt.W=0;
1147 TailDiffuse->Set_Element(i,elt);
1148 }
1149 }
1150 break;
1151 case SAME_AS_HEAD:
1152 // if head is all one color, set tail the same way
1153 if (!Diffuse) {
1156 } else {
1157 // otherwise allocate and copy tail diffuse
1158 if (!TailDiffuse) TailDiffuse=NEW_REF(ShareBufferClass<Vector4>,(MaxNum, "ParticleBufferClass::TailDiffuse"));
1159 VectorProcessorClass::Copy(TailDiffuse->Get_Array(),Diffuse->Get_Array(),MaxNum);
1160 }
1161 break;
1162 default:
1163 WWASSERT(0);
1164 break;
1165 }
1166
1167 if (!TailDiffuse)
1168 LineGroup->Set_Tail_Diffuse(DefaultTailDiffuse);
1169
1170 LineGroup->Set_Arrays(Position[pingpong], TailPosition,Diffuse,TailDiffuse, apt, Size, UCoord, active_point_count);
1172 LineGroup->Render(rinfo);
1173}
1174
1175// Scales the size of the individual particles but doesn't affect their
1176// position (and therefore the size of the particle system as a whole)
1178{
1179 // Scale all size keyframes, keyframe deltas, random size entries,
1180 // MaxSize and SizeRandom.
1181 unsigned int i;
1182 if (NumSizeKeyFrames) {
1183 for (i = 0; i < NumSizeKeyFrames; i++) {
1186 }
1187 } else {
1189 }
1190 if (RandomSizeEntries) {
1191 for (i = 0; i <= NumRandomSizeEntriesMinus1; i++) {
1193 }
1194 }
1195 if (LineRenderer) {
1196 LineRenderer->Scale(scale);
1197 }
1198 MaxSize *= scale;
1199 SizeRandom *= scale;
1200 Accel *= scale;
1201}
1202
1203
1204
1205
1206
1207// The particle buffer never receives a Set_Transform/Position call,
1208// evem though its bounding volume changes. Since bounding volume
1209// invalidations ordinarily occur when these functions are called,
1210// the cached bounding volumes will not be invalidated unless we do
1211// it elsewhere (such as here). We also need to call the particle
1212// emitter's Emit() function (done here to avoid order dependence).
1214{
1215 WWPROFILE("ParticleBufferClass::On_Frame_Update");
1217 if (Emitter) {
1218 Emitter->Emit();
1219 }
1220
1221 if (Is_Complete()) {
1222 WWASSERT(Scene);
1223 Scene->Register(this,SceneClass::RELEASE);
1224 }
1225}
1226
1227
1233
1234
1240
1241
1243{
1244 // This ugly cast is done because the alternative is to make everything
1245 // in the class mutable, which does not seem like a good solution
1246 // (Update_Bounding_Box can potentially update the particle state)
1247 ((ParticleBufferClass *)this)->Update_Bounding_Box();
1248
1249 // The particle buffer's transform is always identity, so
1250 // objspace == worldspace.
1251
1252 // Wrap sphere outside bounding box:
1253 sphere.Center = BoundingBox.Center;
1254 sphere.Radius = BoundingBox.Extent.Length();
1255}
1256
1257
1259{
1260 // This ugly cast is done because the alternative is to make everything
1261 // in the class mutable, which does not seem like a good solution
1262 // (Update_Bounding_Box can potentially update the particle state).
1263 ((ParticleBufferClass *)this)->Update_Bounding_Box();
1264
1265 // The particle buffer's transform is always identity, so
1266 // objspace == worldspace.
1267 box = BoundingBox;
1268}
1269
1270
1272{
1273 if (Is_Not_Hidden_At_All() == false) {
1274 return;
1275 }
1276
1277 // Estimate the screen area of the particle buffer. We shall take the lesser of two
1278 // metrics: the standard bounding-sphere projection (which for many particle systems may
1279 // grossly overestimate the actual screen area), and a measurement based on the screen area of
1280 // individual particles times the maximum number of particles (in the case of densely
1281 // overlapping particles this metric can also give numbers which are too high, which is why we
1282 // use the bounding sphere as backup). Note - to find the area of individual particles we
1283 // treat them as all being the maximum size and being in the center of the bounding sphere).
1284
1285 Vector3 cam = camera.Get_Position();
1286 ViewportClass viewport = camera.Get_Viewport();
1287 Vector2 vpr_min, vpr_max;
1288 camera.Get_View_Plane(vpr_min, vpr_max);
1289 float width_factor = viewport.Width() / (vpr_max.X - vpr_min.X);
1290 float height_factor = viewport.Height() / (vpr_max.Y - vpr_min.Y);
1291
1292 const SphereClass & sphere = Get_Bounding_Sphere();
1293 float dist = (sphere.Center - cam).Length();
1294 float bounding_sphere_projected_radius = 0.0f;
1295 float particle_projected_radius = 0.0f;
1296 if (dist) {
1297 float oo_dist = 1.0f / dist;
1298 bounding_sphere_projected_radius = sphere.Radius * oo_dist;
1299 particle_projected_radius = MaxSize * oo_dist;
1300 }
1301
1302 float bs_rad_sq = bounding_sphere_projected_radius * bounding_sphere_projected_radius;
1303 float p_rad_sq = particle_projected_radius * particle_projected_radius * MaxNum;
1304 float proj_area = WWMATH_PI * MIN(bs_rad_sq, p_rad_sq) * width_factor * height_factor;
1305
1306 // Filter the area over time so we don't get as many pops in the LOD algorithm
1307 ProjectedArea = 0.9f * ProjectedArea + 0.1f * proj_area;
1308
1310
1311 // Ensure lod is no less than minimum allowed
1312 if (Get_LOD_Level() < minlod) Set_LOD_Level(minlod);
1313
1315}
1316
1321
1326
1328{
1329 return(Cost[(LodCount - 1) - DecimationThreshold]);
1330}
1331
1333{
1334 return(Value[(LodCount - 1) - DecimationThreshold]);
1335}
1336
1341
1343{
1344 lod = Bound(lod, 0, (int)LodCount);
1345 DecimationThreshold = (LodCount - 1) - lod;
1346}
1347
1349{
1350 return((LodCount - 1) - DecimationThreshold);
1351}
1352
1354{
1355 return LodCount;
1356}
1357
1358int ParticleBufferClass::Calculate_Cost_Value_Arrays(float screen_area, float *values, float *costs) const
1359{
1360 unsigned int lod = 0;
1361
1362 // Calculate Cost heuristic for each LOD (we currently ignore pixel costs for particle systems)
1363 // The cost factor is later multiplied by the LOD level. The LOD level is the numerator of the
1364 // fraction of particles rendered, where 16 is the denominator. For this reason the cost factor
1365 // is based on a 1/16 (0.0625) of the total.
1366 float cost_factor=0.0f;
1367 switch (RenderMode)
1368 {
1370 cost_factor = (float)MaxNum * 0.0625f;
1371 break;
1373 cost_factor = (float)MaxNum * 2.0f * 0.0625f;
1374 break;
1376 cost_factor = (float) (2*MaxNum-1) * 0.0625f;
1377 break;
1379 cost_factor = (float)MaxNum * 4.0f * 0.0625f;
1380 break;
1382 cost_factor = (float)MaxNum * 8.0f * 0.0625f;
1383 break;
1384 }
1385 for (lod = 0; lod < LodCount; lod++) {
1386 costs[lod] = cost_factor * (float)lod;
1387 // If cost is zero set it to a small nonzero amount to avoid divisions by zero.
1388 costs[lod] = (costs[lod] != 0) ? costs[lod] : 0.000001f;
1389 }
1390
1391 // Calculate Value heuristic. First, all LOD levels for which
1392 // MaxScreenSize is smaller than screen_area have their Value set to
1393 // AT_MIN_LOD, as well as the first LOD after that (unless there are no
1394 // other LODs):
1395 for (lod = 0; lod < LodCount && LODMaxScreenSizes[lod] < screen_area; lod++) {
1396 values[lod] = AT_MIN_LOD;
1397 }
1398
1399 if (lod >= LodCount) {
1400 lod = LodCount - 1;
1401 } else {
1402 values[lod] = AT_MIN_LOD;
1403 }
1404
1405 // Now lod is the lowest allowed - return this value.
1406 int minlod = lod;
1407
1408 // Calculate Value heuristic for any remaining LODs based on normalized screen area:
1409 lod++;
1410 for (; lod < LodCount; lod++) {
1411 // Currently the cost happens to be equal to the poly count. We use a floating-
1412 // point poly count since costs[] contains an approximation to the true polycount which may
1413 // be less than one in some cases (we want to avoid 0 polycounts except for true null LODs)
1414 float polycount = costs[lod];
1415 float benefit_factor = (polycount > WWMATH_EPSILON) ? (1 - (0.5f / (polycount * polycount))) : 0.0f;
1416 values[lod] = (benefit_factor * screen_area * LodBias) / costs[lod];
1417 }
1418 values[LodCount] = AT_MAX_LOD; // Post-inc value will flag max LOD.
1419
1420 return minlod;
1421}
1422
1423
1425{
1426
1427 unsigned int i; // Used in loops
1428 unsigned int ui_previous_key_time = 0;
1429 unsigned int ui_current_key_time = 0;
1430
1431 ColorRandom = new_props.Rand;
1432
1433 // If the randomizer is effectively zero and there are no keyframes, then we just create a
1434 // values array with one entry and store the starting value in it (the keyframes and random
1435 // table will not be used in this case).
1436 static const float eps_byte = 0.0038f; // Epsilon value - less than 1/255
1437 bool color_rand_zero = (fabs(new_props.Rand.X) < eps_byte && fabs(new_props.Rand.Y) < eps_byte && fabs(new_props.Rand.Z) < eps_byte);
1438 if (color_rand_zero && new_props.NumKeyFrames == 0) {
1439
1440 // Release Color, ColorKeyFrameTimes and ColorKeyFrameDeltas if present. Reuse
1441 // ColorKeyFrameValues if the right size, otherwise release and reallocate.
1442 if (Color) {
1443 Color->Release_Ref();
1444 Color = NULL;
1445 }
1446 if (ColorKeyFrameTimes) {
1447 delete [] ColorKeyFrameTimes;
1449 }
1450 if (ColorKeyFrameDeltas) {
1451 delete [] ColorKeyFrameDeltas;
1453 }
1454 if (ColorKeyFrameValues) {
1455 if (NumColorKeyFrames > 1) {
1456 delete [] ColorKeyFrameValues;
1458 }
1459 } else {
1461 }
1462
1465 ColorKeyFrameValues[0] = new_props.Start;
1466
1467 } else {
1468
1469 // Create the color array if not present
1470 if (!Color) {
1471 Color = NEW_REF( ShareBufferClass<Vector3> , (MaxNum, "ParticleBufferClass::Color") );
1472 }
1473
1474 // Check times of color keyframes (each keytime must be larger than the
1475 // previous one by at least a millisecond, and we stop at the first
1476 // keytime of MaxAge or larger. (If all keyframes below MaxAge, color is
1477 // constant during the last segment between last keyframe and MaxAge).
1478 ui_previous_key_time = 0;
1479 for (unsigned int ckey = 0; ckey < new_props.NumKeyFrames; ckey++) {
1480 ui_current_key_time = (unsigned int)(new_props.KeyTimes[ckey] * 1000.0f);
1481 WWASSERT(ui_current_key_time > ui_previous_key_time);
1482 if (ui_current_key_time >= MaxAge) break;
1483 ui_previous_key_time = ui_current_key_time;
1484 }
1485 bool color_constant_at_end = (ckey == new_props.NumKeyFrames);
1486
1487 // Reuse ColorKeyFrameValues, ColorKeyFrameTimes and ColorKeyFrameDeltas if the right size,
1488 // otherwise release and reallocate.
1489 unsigned int new_num_color_key_frames = ckey + 1;// Includes start keyframe (keytime == 0).
1490 if (new_num_color_key_frames != NumColorKeyFrames) {
1491
1492 if (ColorKeyFrameTimes) {
1493 delete [] ColorKeyFrameTimes;
1495 }
1496 if (ColorKeyFrameValues) {
1497 delete [] ColorKeyFrameValues;
1499 }
1500 if (ColorKeyFrameDeltas) {
1501 delete [] ColorKeyFrameDeltas;
1503 }
1504
1505 NumColorKeyFrames = new_num_color_key_frames;
1509 }
1510
1511 // Set color keyframes (deltas will be set later)
1512 ColorKeyFrameTimes[0] = 0;
1513 ColorKeyFrameValues[0] = new_props.Start;
1514 for (i = 1; i < NumColorKeyFrames; i++) {
1515 unsigned int im1 = i - 1;
1516 ColorKeyFrameTimes[i] = (unsigned int)(new_props.KeyTimes[im1] * 1000.0f);
1517 ColorKeyFrameValues[i] = new_props.Values[im1];
1518 }
1519
1520 // Do deltas for all color keyframes except last
1521 for (i = 0; i < NumColorKeyFrames - 1; i++) {
1523 (float)(ColorKeyFrameTimes[i + 1] - ColorKeyFrameTimes[i]);
1524 }
1525
1526 // Do delta for last color keyframe (i is NumColorKeyFrames - 1)
1527 if (color_constant_at_end) {
1528 ColorKeyFrameDeltas[i].Set(0.0, 0.0, 0.0);
1529 } else {
1530 // This is OK because if color_constant_at_end is false, NumColorKeyFrames is equal or
1531 // smaller than color.NumKeyFrames so color.Values[NumColorKeyFrames - 1] and
1532 // color.KeyTimes[NumColorKeyFrames - 1] exist.
1533 ColorKeyFrameDeltas[i] = (new_props.Values[i] - ColorKeyFrameValues[i]) /
1534 (new_props.KeyTimes[i] * 1000.0f - (float)ColorKeyFrameTimes[i]);
1535 }
1536
1537 // Set up color randomizer table
1538
1539 if (color_rand_zero) {
1540
1541 if (RandomColorEntries) {
1542 // Reuse RandomColorEntries if the right size, otherwise release and reallocate.
1543 if (NumRandomColorEntriesMinus1 != 0) {
1544 delete [] RandomColorEntries;
1546 }
1547 } else {
1549 }
1550
1552 RandomColorEntries[0].X = 0.0f;
1553 RandomColorEntries[0].Y = 0.0f;
1554 RandomColorEntries[0].Z = 0.0f;
1555 } else {
1556
1557 // Default size of randomizer tables (tables for non-zero randomizers will be this size)
1558 unsigned int pot_num = Find_POT(MaxNum);
1559 unsigned int default_randomizer_entries = MIN(pot_num, MAX_RANDOM_ENTRIES);
1560
1561 if (RandomColorEntries) {
1562 // Reuse RandomColorEntries if the right size, otherwise release and reallocate.
1563 if (NumRandomColorEntriesMinus1 != (default_randomizer_entries - 1)) {
1564 delete [] RandomColorEntries;
1565 RandomColorEntries = W3DNEWARRAY Vector3 [default_randomizer_entries];
1566 }
1567 } else {
1568 RandomColorEntries = W3DNEWARRAY Vector3 [default_randomizer_entries];
1569 }
1570
1571 NumRandomColorEntriesMinus1 = default_randomizer_entries - 1;
1572
1573 float rscale = new_props.Rand.X * oo_intmax;
1574 float gscale = new_props.Rand.Y * oo_intmax;
1575 float bscale = new_props.Rand.Z * oo_intmax;
1576 for (unsigned int j = 0; j <= NumRandomColorEntriesMinus1; j++) {
1577 RandomColorEntries[j] = Vector3(rand_gen * rscale, rand_gen * gscale, rand_gen * bscale);
1578 }
1579 }
1580 }
1581}
1582
1583
1585{
1586 unsigned int i; // Used in loops
1587 unsigned int ui_previous_key_time = 0;
1588 unsigned int ui_current_key_time = 0;
1589
1590 OpacityRandom = new_props.Rand;
1591
1592 // If the randomizer is effectively zero and there are no keyframes, then we just create a
1593 // values array with one entry and store the starting value in it (the keyframes and random
1594 // table will not be used in this case).
1595 static const float eps_byte = 0.0038f; // Epsilon value - less than 1/255
1596 bool alpha_rand_zero = (fabs(new_props.Rand) < eps_byte);
1597 if (alpha_rand_zero && new_props.NumKeyFrames == 0) {
1598
1599 // Release Alpha, AlphaKeyFrameTimes and AlphaKeyFrameDeltas if present. Reuse
1600 // AlphaKeyFrameValues if the right size, otherwise release and reallocate.
1601 if (Alpha) {
1602 Alpha->Release_Ref();
1603 Alpha = NULL;
1604 }
1605 if (AlphaKeyFrameTimes) {
1606 delete [] AlphaKeyFrameTimes;
1608 }
1609 if (AlphaKeyFrameDeltas) {
1610 delete [] AlphaKeyFrameDeltas;
1612 }
1613 if (AlphaKeyFrameValues) {
1614 if (NumAlphaKeyFrames > 1) {
1615 delete [] AlphaKeyFrameValues;
1616 AlphaKeyFrameValues = W3DNEWARRAY float [1];
1617 }
1618 } else {
1619 AlphaKeyFrameValues = W3DNEWARRAY float [1];
1620 }
1621
1624 AlphaKeyFrameValues[0] = new_props.Start;
1625
1626 } else {
1627
1628 // Create the alpha array if not present
1629 if (!Alpha) {
1630 Alpha = NEW_REF( ShareBufferClass<float> , (MaxNum, "ParticleBufferClass::Alpha") );
1631 }
1632
1633 // Check times of opacity keyframes (each keytime must be larger than the
1634 // previous one by at least a millisecond, and we stop at the first
1635 // keytime of MaxAge or larger. (If all keyframes below MaxAge, alpha is
1636 // constant during the last segment between last keyframe and MaxAge).
1637 ui_previous_key_time = 0;
1638 for (unsigned int akey = 0; akey < new_props.NumKeyFrames; akey++) {
1639 ui_current_key_time = (unsigned int)(new_props.KeyTimes[akey] * 1000.0f);
1640 WWASSERT(ui_current_key_time > ui_previous_key_time);
1641 if (ui_current_key_time >= MaxAge) break;
1642 ui_previous_key_time = ui_current_key_time;
1643 }
1644 bool alpha_constant_at_end = (akey == new_props.NumKeyFrames);
1645
1646 // Reuse AlphaKeyFrameValues, AlphaKeyFrameTimes and AlphaKeyFrameDeltas if the right size,
1647 // otherwise release and reallocate.
1648 unsigned int new_num_alpha_key_frames = akey + 1;// Includes start keyframe (keytime == 0).
1649 if (new_num_alpha_key_frames != NumAlphaKeyFrames) {
1650
1651 if (AlphaKeyFrameTimes) {
1652 delete [] AlphaKeyFrameTimes;
1654 }
1655 if (AlphaKeyFrameValues) {
1656 delete [] AlphaKeyFrameValues;
1658 }
1659 if (AlphaKeyFrameDeltas) {
1660 delete [] AlphaKeyFrameDeltas;
1662 }
1663
1664 NumAlphaKeyFrames = new_num_alpha_key_frames;
1668 }
1669
1670 // Set alpha keyframes (deltas will be set later)
1671 AlphaKeyFrameTimes[0] = 0;
1672 AlphaKeyFrameValues[0] = new_props.Start;
1673 for (i = 1; i < NumAlphaKeyFrames; i++) {
1674 unsigned int im1 = i - 1;
1675 AlphaKeyFrameTimes[i] = (unsigned int)(new_props.KeyTimes[im1] * 1000.0f);
1676 AlphaKeyFrameValues[i] = new_props.Values[im1];
1677 }
1678
1679 // Do deltas for all alpha keyframes except last
1680 for (i = 0; i < NumAlphaKeyFrames - 1; i++) {
1682 (float)(AlphaKeyFrameTimes[i + 1] - AlphaKeyFrameTimes[i]);
1683 }
1684
1685 // Do delta for last alpha keyframe (i is NumAlphaKeyFrames - 1)
1686 if (alpha_constant_at_end) {
1687 AlphaKeyFrameDeltas[i] = 0.0f;
1688 } else {
1689 // This is OK because if alpha_constant_at_end is false, NumAlphaKeyFrames is equal or
1690 // smaller than opacity.NumKeyFrames so opacity.Values[NumAlphaKeyFrames - 1] and
1691 // opacity.KeyTimes[NumAlphaKeyFrames - 1] exist.
1692 AlphaKeyFrameDeltas[i] = (new_props.Values[i] - AlphaKeyFrameValues[i]) /
1693 (new_props.KeyTimes[i] * 1000.0f - (float)AlphaKeyFrameTimes[i]);
1694 }
1695
1696 // Set up alpha randomizer table
1697
1698 if (alpha_rand_zero) {
1699
1700 if (RandomAlphaEntries) {
1701 // Reuse RandomAlphaEntries if the right size, otherwise release and reallocate.
1702 if (NumRandomAlphaEntriesMinus1 != 0) {
1703 delete [] RandomAlphaEntries;
1704 RandomAlphaEntries = W3DNEWARRAY float [1];
1705 }
1706 } else {
1707 RandomAlphaEntries = W3DNEWARRAY float [1];
1708 }
1709
1711 RandomAlphaEntries[0] = 0.0f;
1712 } else {
1713
1714 // Default size of randomizer tables (tables for non-zero randomizers will be this size)
1715 unsigned int pot_num = Find_POT(MaxNum);
1716 unsigned int default_randomizer_entries = MIN(pot_num, MAX_RANDOM_ENTRIES);
1717
1718 if (RandomAlphaEntries) {
1719 // Reuse RandomAlphaEntries if the right size, otherwise release and reallocate.
1720 if (NumRandomAlphaEntriesMinus1 != (default_randomizer_entries - 1)) {
1721 delete [] RandomAlphaEntries;
1722 RandomAlphaEntries = W3DNEWARRAY float [default_randomizer_entries];
1723 }
1724 } else {
1725 RandomAlphaEntries = W3DNEWARRAY float [default_randomizer_entries];
1726 }
1727
1728 NumRandomAlphaEntriesMinus1 = default_randomizer_entries - 1;
1729
1730 float ascale = new_props.Rand * oo_intmax;
1731 for (unsigned int j = 0; j <= NumRandomAlphaEntriesMinus1; j++) {
1732 RandomAlphaEntries[j] = rand_gen * ascale;
1733 }
1734 }
1735 }
1736}
1737
1738
1740{
1741
1742 unsigned int i; // Used in loops
1743 unsigned int ui_previous_key_time = 0;
1744 unsigned int ui_current_key_time = 0;
1745
1746 SizeRandom = new_props.Rand;
1747
1748 // If the randomizer is effectively zero and there are no keyframes, then we just create a
1749 // values array with one entry and store the starting value in it (the keyframes and random
1750 // table will not be used in this case).
1751 static const float eps_size = 1.0e-12f; // Size scale unknown so must use very small epsilon
1752 bool size_rand_zero = (fabs(new_props.Rand) < eps_size);
1753 if (size_rand_zero && new_props.NumKeyFrames == 0) {
1754
1755 // Release Size, SizeKeyFrameTimes and SizeaKeyFrameDeltas if present. Reuse
1756 // SizeKeyFrameValues if the right size, otherwise release and reallocate.
1757 if (Size) {
1758 Size->Release_Ref();
1759 Size = NULL;
1760 }
1761 if (SizeKeyFrameTimes) {
1762 delete [] SizeKeyFrameTimes;
1764 }
1765 if (SizeKeyFrameDeltas) {
1766 delete [] SizeKeyFrameDeltas;
1768 }
1769 if (SizeKeyFrameValues) {
1770 if (NumSizeKeyFrames > 1) {
1771 delete [] SizeKeyFrameValues;
1772 SizeKeyFrameValues = W3DNEWARRAY float [1];
1773 }
1774 } else {
1775 SizeKeyFrameValues = W3DNEWARRAY float [1];
1776 }
1777
1778 NumSizeKeyFrames = 0;
1780 SizeKeyFrameValues[0] = new_props.Start;
1782 } else {
1783
1784 // Create the size array if not present
1785 if (!Size) {
1786 Size = NEW_REF( ShareBufferClass<float> , (MaxNum, "ParticleBufferClass::Size") );
1787 }
1788
1789 // Check times of size keyframes (each keytime must be larger than the
1790 // previous one by at least a millisecond, and we stop at the first
1791 // keytime of MaxAge or larger. (If all keyframes below MaxAge, size is
1792 // constant during the last segment between last keyframe and MaxAge).
1793 ui_previous_key_time = 0;
1794 for (unsigned int skey = 0; skey < new_props.NumKeyFrames; skey++) {
1795 ui_current_key_time = (unsigned int)(new_props.KeyTimes[skey] * 1000.0f);
1796 WWASSERT(ui_current_key_time > ui_previous_key_time);
1797 if (ui_current_key_time >= MaxAge) break;
1798 ui_previous_key_time = ui_current_key_time;
1799 }
1800 bool size_constant_at_end = (skey == new_props.NumKeyFrames);
1801
1802 // Reuse SizeKeyFrameValues, SizeKeyFrameTimes and SizeKeyFrameDeltas if the right size,
1803 // otherwise release and reallocate.
1804 unsigned int new_num_size_key_frames = skey + 1;// Includes start keyframe (keytime == 0).
1805 if (new_num_size_key_frames != NumSizeKeyFrames) {
1806
1807 if (SizeKeyFrameTimes) {
1808 delete [] SizeKeyFrameTimes;
1810 }
1811 if (SizeKeyFrameValues) {
1812 delete [] SizeKeyFrameValues;
1814 }
1815 if (SizeKeyFrameDeltas) {
1816 delete [] SizeKeyFrameDeltas;
1818 }
1819
1820 NumSizeKeyFrames = new_num_size_key_frames;
1824 }
1825
1826 // Set size keyframes (deltas will be set later)
1827 SizeKeyFrameTimes[0] = 0;
1828 SizeKeyFrameValues[0] = new_props.Start;
1829 for (i = 1; i < NumSizeKeyFrames; i++) {
1830 unsigned int im1 = i - 1;
1831 SizeKeyFrameTimes[i] = (unsigned int)(new_props.KeyTimes[im1] * 1000.0f);
1832 SizeKeyFrameValues[i] = new_props.Values[im1];
1833 }
1834
1835 // Do deltas for all size keyframes except last
1836 for (i = 0; i < NumSizeKeyFrames - 1; i++) {
1838 (float)(SizeKeyFrameTimes[i + 1] - SizeKeyFrameTimes[i]);
1839 }
1840
1841 // Do delta for last size keyframe (i is NumSizeKeyFrames - 1)
1842 if (size_constant_at_end) {
1843 SizeKeyFrameDeltas[i] = 0.0f;
1844 } else {
1845 // This is OK because if size_constant_at_end is false, NumSizeKeyFrames is equal or
1846 // smaller than new_props.NumKeyFrames so new_props.Values[NumSizeKeyFrames - 1] and
1847 // new_props.KeyTimes[NumSizeKeyFrames - 1] exist.
1848 SizeKeyFrameDeltas[i] = (new_props.Values[i] - SizeKeyFrameValues[i]) /
1849 (new_props.KeyTimes[i] * 1000.0f - (float)SizeKeyFrameTimes[i]);
1850 }
1851
1852 // Find maximum size (for BBox updates)
1854 for (i = 1; i < NumSizeKeyFrames; i++) {
1856 }
1857 // If last delta is positive, there may be a larger size keyframe:
1860 MaxSize = MAX(MaxSize, last_size);
1861 MaxSize += fabs(new_props.Rand);
1862
1863 // Set up size randomizer table
1864
1865 if (size_rand_zero) {
1866
1867 if (RandomSizeEntries) {
1868 // Reuse RandomSizeEntries if the right size, otherwise release and reallocate.
1869 if (NumRandomSizeEntriesMinus1 != 0) {
1870 delete [] RandomSizeEntries;
1871 RandomSizeEntries = W3DNEWARRAY float [1];
1872 }
1873 } else {
1874 RandomSizeEntries = W3DNEWARRAY float [1];
1875 }
1876
1878 RandomSizeEntries[0] = 0.0f;
1879 } else {
1880
1881 // Default size of randomizer tables (tables for non-zero randomizers will be this size)
1882 unsigned int pot_num = Find_POT(MaxNum);
1883 unsigned int default_randomizer_entries = MIN(pot_num, MAX_RANDOM_ENTRIES);
1884
1885 if (RandomSizeEntries) {
1886 // Reuse RandomSizeEntries if the right size, otherwise release and reallocate.
1887 if (NumRandomSizeEntriesMinus1 != (default_randomizer_entries - 1)) {
1888 delete [] RandomSizeEntries;
1889 RandomSizeEntries = W3DNEWARRAY float [default_randomizer_entries];
1890 }
1891 } else {
1892 RandomSizeEntries = W3DNEWARRAY float [default_randomizer_entries];
1893 }
1894
1895 NumRandomSizeEntriesMinus1 = default_randomizer_entries - 1;
1896
1897 float sscale = new_props.Rand * oo_intmax;
1898 for (unsigned int j = 0; j <= NumRandomSizeEntriesMinus1; j++) {
1899 RandomSizeEntries[j] = rand_gen * sscale;
1900 }
1901 }
1902 }
1903}
1904
1905
1907{
1908
1909 unsigned int i; // Used in loops
1910 float oo_intmax = 1.0f / (float)INT_MAX;
1911 unsigned int ui_previous_key_time = 0;
1912 unsigned int ui_current_key_time = 0;
1913
1914 /*
1915 ** NOTE: Input rotations are in rotations per second. These will be converted to rotations per millisecond.
1916 */
1917
1918 RotationRandom = new_props.Rand * 0.001f;
1919 InitialOrientationRandom = orient_rnd;
1920
1921 // If both randomizers are effectively zero and rotation is constant zero, then all arrays are NULL.
1922 static const float eps_orientation = 2.77777778e-4f; // Epsilon is equivalent to 0.1 degree
1923 static const float eps_rotation = 2.77777778e-4f; // Epsilon is equivalent to one rotation per hour (in rotations / second)
1924 bool orientation_rand_zero = fabs(orient_rnd) < eps_orientation;
1925 bool rotation_rand_zero = fabs(new_props.Rand) < eps_rotation;
1926 if (orientation_rand_zero && rotation_rand_zero && new_props.NumKeyFrames == 0 && fabs(new_props.Start) < eps_rotation) {
1927
1928 // Release Arrays,
1931 delete [] RotationKeyFrameTimes;
1933 }
1937 }
1939 delete [] RotationKeyFrameValues;
1941 }
1943 delete [] OrientationKeyFrameValues;
1945 }
1946
1950
1951 } else {
1952
1953 // Create the array if not present
1954 if (!Orientation) {
1955 Orientation = NEW_REF( ShareBufferClass<uint8> , (MaxNum, "ParticleBufferClass::Orientation") );
1956 }
1957
1958 // Check times of the keyframes (each keytime must be larger than the
1959 // previous one by at least a millisecond, and we stop at the first
1960 // keytime of MaxAge or larger. (If all keyframes below MaxAge, the value is
1961 // constant during the last segment between last keyframe and MaxAge).
1962 ui_previous_key_time = 0;
1963 for (unsigned int key = 0; key < new_props.NumKeyFrames; key++) {
1964 ui_current_key_time = (unsigned int)(new_props.KeyTimes[key] * 1000.0f);
1965 WWASSERT(ui_current_key_time > ui_previous_key_time);
1966 if (ui_current_key_time >= MaxAge) break;
1967 ui_previous_key_time = ui_current_key_time;
1968 }
1969 bool rotation_constant_at_end = (key == new_props.NumKeyFrames);
1970
1971 // Reuse RotationKeyFrameValues, RotationKeyFrameTimes, RotationKeyFrameDeltas and
1972 // OrientationKeyFrameValues if the right size, otherwise release and reallocate.
1973 unsigned int new_num_key_frames = key + 1;// Includes start keyframe (keytime == 0).
1974 if (new_num_key_frames != NumRotationKeyFrames) {
1975
1977 delete [] RotationKeyFrameTimes;
1979 }
1981 delete [] RotationKeyFrameValues;
1983 }
1987 }
1989 delete [] OrientationKeyFrameValues;
1991 }
1992
1993 NumRotationKeyFrames = new_num_key_frames;
1998 }
1999
2000 // Set rotation keyframes (deltas will be set later)
2001 RotationKeyFrameTimes[0] = 0;
2002 RotationKeyFrameValues[0] = new_props.Start * 0.001f;
2003 for (i = 1; i < NumRotationKeyFrames; i++) {
2004 unsigned int im1 = i - 1;
2005 RotationKeyFrameTimes[i] = (unsigned int)(new_props.KeyTimes[im1] * 1000.0f);
2006 RotationKeyFrameValues[i] = new_props.Values[im1] * 0.001f;
2007 }
2008
2009 // Do deltas for all rotation keyframes except last
2010 for (i = 0; i < NumRotationKeyFrames - 1; i++) {
2012 (float)(RotationKeyFrameTimes[i + 1] - RotationKeyFrameTimes[i]) );
2013 }
2014
2015 // Do delta for last rotation keyframe (i is NumRotationKeyFrames - 1)
2016 if (rotation_constant_at_end) {
2018 } else {
2019 // This is OK because if rotation_constant_at_end is false, NumRotationKeyFrames is equal or
2020 // smaller than new_props.NumKeyFrames so new_props.Values[NumRotationKeyFrames - 1] and
2021 // new_props.KeyTimes[NumRotationKeyFrames - 1] exist.
2022 HalfRotationKeyFrameDeltas[i] = 0.5f * (new_props.Values[i] * 0.001f - RotationKeyFrameValues[i]) /
2023 (new_props.KeyTimes[i] * 1000.0f - (float)RotationKeyFrameTimes[i]);
2024 }
2025
2026 // Calculate orientation keyframes by integrating the rotation at each keyframe
2027 OrientationKeyFrameValues[0] = 0.0f;
2028 for (i = 1; i < NumRotationKeyFrames; i++) {
2029 float delta_t = (float)(RotationKeyFrameTimes[i] - RotationKeyFrameTimes[i - 1]);
2031 (RotationKeyFrameValues[i - 1] + HalfRotationKeyFrameDeltas[i - 1] * delta_t);
2032 }
2033
2034 // Set up rotation randomizer table
2035 if (rotation_rand_zero) {
2036
2038 // Reuse RandomRotationEntries if the right size, otherwise release and reallocate.
2040 delete [] RandomRotationEntries;
2042 }
2043 } else {
2045 }
2046
2048 RandomRotationEntries[0] = 0.0f;
2049 } else {
2050
2051 // Default size of randomizer tables (tables for non-zero randomizers will be this size)
2052 unsigned int pot_num = Find_POT(MaxNum);
2053 unsigned int default_randomizer_entries = MIN(pot_num, MAX_RANDOM_ENTRIES);
2054
2056 // Reuse RandomRotationEntries if the right size, otherwise release and reallocate.
2057 if (NumRandomRotationEntriesMinus1 != (default_randomizer_entries - 1)) {
2058 delete [] RandomRotationEntries;
2059 RandomRotationEntries = W3DNEWARRAY float [default_randomizer_entries];
2060 }
2061 } else {
2062 RandomRotationEntries = W3DNEWARRAY float [default_randomizer_entries];
2063 }
2064
2065 NumRandomRotationEntriesMinus1 = default_randomizer_entries - 1;
2066
2067 float scale = new_props.Rand * 0.001f * oo_intmax;
2068 for (unsigned int j = 0; j <= NumRandomRotationEntriesMinus1; j++) {
2069 RandomRotationEntries[j] = rand_gen * scale;
2070 }
2071 }
2072
2073 // Set up orientation randomizer table
2074 if (orientation_rand_zero) {
2075
2077 // Reuse RandomOrientationEntries if the right size, otherwise release and reallocate.
2079 delete [] RandomOrientationEntries;
2081 }
2082 } else {
2084 }
2085
2087 RandomOrientationEntries[0] = 0.0f;
2088 } else {
2089
2090 // Default size of randomizer tables (tables for non-zero randomizers will be this size)
2091 unsigned int pot_num = Find_POT(MaxNum);
2092 unsigned int default_randomizer_entries = MIN(pot_num, MAX_RANDOM_ENTRIES);
2093
2095 // Reuse RandomOrientationEntries if the right size, otherwise release and reallocate.
2096 if (NumRandomOrientationEntriesMinus1 != (default_randomizer_entries - 1)) {
2097 delete [] RandomOrientationEntries;
2098 RandomOrientationEntries = W3DNEWARRAY float [default_randomizer_entries];
2099 }
2100 } else {
2101 RandomOrientationEntries = W3DNEWARRAY float [default_randomizer_entries];
2102 }
2103
2104 NumRandomOrientationEntriesMinus1 = default_randomizer_entries - 1;
2105
2106 float scale = orient_rnd * oo_intmax;
2107 for (unsigned int j = 0; j <= NumRandomOrientationEntriesMinus1; j++) {
2108 RandomOrientationEntries[j] = rand_gen * scale;
2109 }
2110 }
2111 }
2112}
2113
2114
2115
2117{
2118
2119 unsigned int i; // Used in loops
2120 float oo_intmax = 1.0f / (float)INT_MAX;
2121 unsigned int ui_previous_key_time = 0;
2122 unsigned int ui_current_key_time = 0;
2123
2124 FrameRandom = new_props.Rand;
2125
2126 // If the randomizer is effectively zero and there are no keyframes, then we just create a
2127 // values array with one entry and store the starting value in it (the keyframes and random
2128 // table will not be used in this case).
2129 static const float eps_frame = 0.1f; // Epsilon is equivalent to 0.1 frame
2130 bool frame_rand_zero = (fabs(new_props.Rand) < eps_frame);
2131 if (frame_rand_zero && new_props.NumKeyFrames == 0) {
2132
2133 // Release Arrays, Reuse KeyFrameValues if the right size,
2134 // otherwise release and reallocate.
2137 if (FrameKeyFrameTimes) {
2138 delete [] FrameKeyFrameTimes;
2140 }
2141 if (FrameKeyFrameDeltas) {
2142 delete [] FrameKeyFrameDeltas;
2144 }
2145 if (FrameKeyFrameValues) {
2146 if (NumFrameKeyFrames > 1) {
2147 delete [] FrameKeyFrameValues;
2148 FrameKeyFrameValues = W3DNEWARRAY float [1];
2149 }
2150 } else {
2151 FrameKeyFrameValues = W3DNEWARRAY float [1];
2152 }
2153
2156 FrameKeyFrameValues[0] = new_props.Start;
2157
2158 } else {
2159
2160 // Create the array if not present
2163 if (!UCoord) {
2164 UCoord = NEW_REF( ShareBufferClass<float>, (MaxNum, "ParticleBufferClass::UCoord") );
2165 }
2166 } else {
2167 if (!Frame) {
2168 Frame = NEW_REF( ShareBufferClass<uint8> , (MaxNum, "ParticleBufferClass::Frame") );
2169 }
2170 }
2171
2172 // Check times of the keyframes (each keytime must be larger than the
2173 // previous one by at least a millisecond, and we stop at the first
2174 // keytime of MaxAge or larger. (If all keyframes below MaxAge, the value is
2175 // constant during the last segment between last keyframe and MaxAge).
2176 ui_previous_key_time = 0;
2177 for (unsigned int key = 0; key < new_props.NumKeyFrames; key++) {
2178 ui_current_key_time = (unsigned int)(new_props.KeyTimes[key] * 1000.0f);
2179 WWASSERT(ui_current_key_time > ui_previous_key_time);
2180 if (ui_current_key_time >= MaxAge) break;
2181 ui_previous_key_time = ui_current_key_time;
2182 }
2183 bool frame_constant_at_end = (key == new_props.NumKeyFrames);
2184
2185 // Reuse FrameKeyFrameValues, FrameKeyFrameTimes and FrameKeyFrameDeltas if the right size,
2186 // otherwise release and reallocate.
2187 unsigned int new_num_key_frames = key + 1;// Includes start keyframe (keytime == 0).
2188 if (new_num_key_frames != NumFrameKeyFrames) {
2189
2190 if (FrameKeyFrameTimes) {
2191 delete [] FrameKeyFrameTimes;
2193 }
2194 if (FrameKeyFrameValues) {
2195 delete [] FrameKeyFrameValues;
2197 }
2198 if (FrameKeyFrameDeltas) {
2199 delete [] FrameKeyFrameDeltas;
2201 }
2202
2203 NumFrameKeyFrames = new_num_key_frames;
2207 }
2208
2209 // Set keyframes (deltas will be set later)
2210 FrameKeyFrameTimes[0] = 0;
2211 FrameKeyFrameValues[0] = new_props.Start;
2212 for (i = 1; i < NumFrameKeyFrames; i++) {
2213 unsigned int im1 = i - 1;
2214 FrameKeyFrameTimes[i] = (unsigned int)(new_props.KeyTimes[im1] * 1000.0f);
2215 FrameKeyFrameValues[i] = new_props.Values[im1];
2216 }
2217
2218 // Do deltas for all frame keyframes except last
2219 for (i = 0; i < NumFrameKeyFrames - 1; i++) {
2221 (float)(FrameKeyFrameTimes[i + 1] - FrameKeyFrameTimes[i]);
2222 }
2223
2224 // Do delta for last frame keyframe (i is NumFrameKeyFrames - 1)
2225 if (frame_constant_at_end) {
2226 FrameKeyFrameDeltas[i] = 0.0f;
2227 } else {
2228 // This is OK because if frame_constant_at_end is false, NumFrameKeyFrames is equal or
2229 // smaller than new_props.NumKeyFrames so new_props.Values[NumFrameKeyFrames - 1] and
2230 // new_props.KeyTimes[NumFrameKeyFrames - 1] exist.
2231 FrameKeyFrameDeltas[i] = (new_props.Values[i] - FrameKeyFrameValues[i]) /
2232 (new_props.KeyTimes[i] * 1000.0f - (float)FrameKeyFrameTimes[i]);
2233 }
2234
2235 // Set up frame randomizer table
2236 if (frame_rand_zero) {
2237
2238 if (RandomFrameEntries) {
2239 // Reuse RandomFrameEntries if the right size, otherwise release and reallocate.
2240 if (NumRandomFrameEntriesMinus1 != 0) {
2241 delete [] RandomFrameEntries;
2242 RandomFrameEntries = W3DNEWARRAY float [1];
2243 }
2244 } else {
2245 RandomFrameEntries = W3DNEWARRAY float [1];
2246 }
2247
2249 RandomFrameEntries[0] = 0.0f;
2250 } else {
2251
2252 // Default size of randomizer tables (tables for non-zero randomizers will be this size)
2253 unsigned int pot_num = Find_POT(MaxNum);
2254 unsigned int default_randomizer_entries = MIN(pot_num, MAX_RANDOM_ENTRIES);
2255
2256 if (RandomFrameEntries) {
2257 // Reuse RandomFrameEntries if the right size, otherwise release and reallocate.
2258 if (NumRandomFrameEntriesMinus1 != (default_randomizer_entries - 1)) {
2259 delete [] RandomFrameEntries;
2260 RandomFrameEntries = W3DNEWARRAY float [default_randomizer_entries];
2261 }
2262 } else {
2263 RandomFrameEntries = W3DNEWARRAY float [default_randomizer_entries];
2264 }
2265
2266 NumRandomFrameEntriesMinus1 = default_randomizer_entries - 1;
2267
2268 float scale = new_props.Rand * oo_intmax;
2269 for (unsigned int j = 0; j <= NumRandomFrameEntriesMinus1; j++) {
2270 RandomFrameEntries[j] = rand_gen * scale;
2271 }
2272 }
2273 }
2274}
2275
2276
2278{
2279
2280 unsigned int i; // Used in loops
2281 float oo_intmax = 1.0f / (float)INT_MAX;
2282 unsigned int ui_previous_key_time = 0;
2283 unsigned int ui_current_key_time = 0;
2284
2285 BlurTimeRandom = new_blur_times.Rand;
2286
2287 // If the randomizer is effectively zero and there are no keyframes, then we just create a
2288 // values array with one entry and store the starting value in it (the keyframes and random
2289 // table will not be used in this case).
2290 static const float eps_blur = 1e-5f; // Epsilon is equivalent to 1e-5 units per second
2291 bool blurtime_rand_zero = (fabs(new_blur_times.Rand) < eps_blur);
2292 if (blurtime_rand_zero && new_blur_times.NumKeyFrames == 0) {
2293
2294 // Release Arrays, Reuse KeyFrameValues if the right size,
2295 // otherwise release and reallocate.
2297 delete [] BlurTimeKeyFrameTimes;
2299 }
2301 delete [] BlurTimeKeyFrameDeltas;
2303 }
2305 if (NumBlurTimeKeyFrames > 1) {
2306 delete [] BlurTimeKeyFrameValues;
2307 BlurTimeKeyFrameValues = new float [1];
2308 }
2309 } else {
2310 BlurTimeKeyFrameValues = new float [1];
2311 }
2312
2315 BlurTimeKeyFrameValues[0] = new_blur_times.Start;
2316
2317 } else {
2318
2319 // Check times of the keyframes (each keytime must be larger than the
2320 // previous one by at least a millisecond, and we stop at the first
2321 // keytime of MaxAge or larger. (If all keyframes below MaxAge, the value is
2322 // constant during the last segment between last keyframe and MaxAge).
2323 ui_previous_key_time = 0;
2324 for (unsigned int key = 0; key < new_blur_times.NumKeyFrames; key++) {
2325 ui_current_key_time = (unsigned int)(new_blur_times.KeyTimes[key] * 1000.0f);
2326 WWASSERT(ui_current_key_time > ui_previous_key_time);
2327 if (ui_current_key_time >= MaxAge) break;
2328 ui_previous_key_time = ui_current_key_time;
2329 }
2330 bool blurtime_constant_at_end = (key == new_blur_times.NumKeyFrames);
2331
2332 // Reuse BlurTimeKeyFrameValues, BlurTimeKeyFrameTimes and BlurTimeKeyFrameDeltas if the right size,
2333 // otherwise release and reallocate.
2334 unsigned int new_num_key_frames = key + 1;// Includes start keyframe (keytime == 0).
2335 if (new_num_key_frames != NumBlurTimeKeyFrames) {
2336
2338 delete [] BlurTimeKeyFrameTimes;
2340 }
2342 delete [] BlurTimeKeyFrameValues;
2344 }
2346 delete [] BlurTimeKeyFrameDeltas;
2348 }
2349
2350 NumBlurTimeKeyFrames = new_num_key_frames;
2351 BlurTimeKeyFrameTimes = new unsigned int [NumBlurTimeKeyFrames];
2354 }
2355
2356 // Set keyframes (deltas will be set later)
2357 BlurTimeKeyFrameTimes[0] = 0;
2358 BlurTimeKeyFrameValues[0] = new_blur_times.Start;
2359 for (i = 1; i < NumBlurTimeKeyFrames; i++) {
2360 unsigned int im1 = i - 1;
2361 BlurTimeKeyFrameTimes[i] = (unsigned int)(new_blur_times.KeyTimes[im1] * 1000.0f);
2362 BlurTimeKeyFrameValues[i] = new_blur_times.Values[im1];
2363 }
2364
2365 // Do deltas for all frame keyframes except last
2366 for (i = 0; i < NumBlurTimeKeyFrames - 1; i++) {
2368 (float)(BlurTimeKeyFrameTimes[i + 1] - BlurTimeKeyFrameTimes[i]);
2369 }
2370
2371 // Do delta for last frame keyframe (i is NumBlurTimeKeyFrames - 1)
2372 if (blurtime_constant_at_end) {
2373 BlurTimeKeyFrameDeltas[i] = 0.0f;
2374 } else {
2375 // This is OK because if frame_constant_at_end is false, NumBlurTimeKeyFrames is equal or
2376 // smaller than new_props.NumKeyFrames so new_props.Values[NumBlurTimeKeyFrames - 1] and
2377 // new_props.KeyTimes[NumBlurTimeKeyFrames - 1] exist.
2378 BlurTimeKeyFrameDeltas[i] = (new_blur_times.Values[i] - BlurTimeKeyFrameValues[i]) /
2379 (new_blur_times.KeyTimes[i] * 1000.0f - (float)BlurTimeKeyFrameTimes[i]);
2380 }
2381
2382 // Set up frame randomizer table
2383 if (blurtime_rand_zero) {
2384
2386 // Reuse RandomBlurTimeEntries if the right size, otherwise release and reallocate.
2388 delete [] RandomBlurTimeEntries;
2389 RandomBlurTimeEntries = new float [1];
2390 }
2391 } else {
2392 RandomBlurTimeEntries = new float [1];
2393 }
2394
2396 RandomBlurTimeEntries[0] = 0.0f;
2397 } else {
2398
2399 // Default size of randomizer tables (tables for non-zero randomizers will be this size)
2400 unsigned int pot_num = Find_POT(MaxNum);
2401 unsigned int default_randomizer_entries = MIN(pot_num, MAX_RANDOM_ENTRIES);
2402
2404 // Reuse RandomBlurTimeEntries if the right size, otherwise release and reallocate.
2405 if (NumRandomBlurTimeEntriesMinus1 != (default_randomizer_entries - 1)) {
2406 delete [] RandomBlurTimeEntries;
2407 RandomBlurTimeEntries = new float [default_randomizer_entries];
2408 }
2409 } else {
2410 RandomBlurTimeEntries = new float [default_randomizer_entries];
2411 }
2412
2413 NumRandomBlurTimeEntriesMinus1 = default_randomizer_entries - 1;
2414
2415 float scale = new_blur_times.Rand * oo_intmax;
2416 for (unsigned int j = 0; j <= NumRandomBlurTimeEntriesMinus1; j++) {
2417 RandomBlurTimeEntries[j] = rand_gen * scale;
2418 }
2419 }
2420 }
2421}
2422
2423
2424// This informs the buffer that the emitter is dead, so it can release
2425// its pointer to it and be removed itself after all its particles dies
2426// out.
2428{
2429 IsEmitterDead = true;
2430 // We do not have a ref for the emitter (see DTor for detailed explanation)
2431 // Emitter->Release_Ref();
2432 Emitter = NULL;
2433}
2434
2435
2436// This set's the buffer's current emitter - this should usually be
2437// called only by the emitter's copy constructor after it clones a
2438// buffer.
2440{
2441 if (Emitter) {
2442 // We do not have a ref for the emitter (see DTor for detailed explanation)
2443 // Emitter->Release_Ref();
2444 Emitter = NULL;
2445 }
2446
2447 Emitter = emitter;
2448
2449 if (Emitter) {
2450 // We do not add a ref for the emitter (see DTor for detailed explanation)
2451 // Emitter->Add_Ref();
2452 }
2453}
2454
2455
2457{
2458 // Note that this function does not initialize the new particle - it
2459 // returns its address to a different function which performs the actual
2460 // initialization.
2461
2462 // Push new particle on new particle queue. If it overflows, just adjust
2463 // queue to remove oldest member (which is the one which was overwritten).
2466 if (++NewParticleQueueCount == (signed)(MaxNum + 1)) {
2467 // Overflow - advance queue start:
2470 }
2471
2472 return ptr;
2473}
2474
2475
2477{
2478 // This ugly cast is done because the alternative is to make everything
2479 // in the class mutable, which does not seem like a good solution
2480 // (Update_Bounding_Box can potentially update the particle state).
2481 ((ParticleBufferClass *)this)->Update_Bounding_Box();
2482
2483 // Update cached bounding box and sphere according to the bounding box:
2484 CachedBoundingSphere.Init(BoundingBox.Center, BoundingBox.Extent.Length());
2487}
2488
2489
2491{
2492 // Note: elapsed may be very large indeed the first time the object is
2493 // updated, but this doesn't matter, since it is actually only used in
2494 // Update_Non_New_Particles(), which is never called on the first update.
2495 unsigned int elapsed = WW3D::Get_Sync_Time() - LastUpdateTime;
2496 if (elapsed == 0U) return;
2497
2498 // Get new particles from the input buffer and write them into the circular
2499 // particle buffer, possibly overwriting older particles. Update each
2500 // according to its age.
2502
2503 // Kill all remaining particles which will pass their max age this update.
2505
2506 // Update all living, non-new particles by a uniform time interval.
2507 if (NonNewNum > 0) Update_Non_New_Particles(elapsed);
2508
2509 // Mark all new particles as non-new.
2510 End = NewEnd;
2511 NonNewNum += NewNum;
2512 NewNum = 0;
2513
2515
2516 BoundingBoxDirty = true;
2517}
2518
2519
2521{
2522 // NOTE: The visual state (color/alpha/size) is "stateless" in that each time it is calculated
2523 // without referring to what it was before. This is important for when we optimize the particle
2524 // systems/pointgroups in the future to chunk triangles into reusable small buffers.
2525
2526 // If all visual state is constant do nothing.
2527 // Linegroup modes have a visual state that always have to be updated though
2528 bool is_linegroup=( (RenderMode==W3D_EMITTER_RENDER_MODE_LINEGRP_TETRA) ||
2530 if (!Color && !Alpha && !Size && !Orientation && !Frame && !UCoord && !is_linegroup) return;
2531
2532 // In the general case, a range in a circular buffer can be composed of up
2533 // to two subranges. Find the Start - End subranges.
2534 unsigned int sub1_end; // End of subrange 1.
2535 unsigned int sub2_start; // Start of subrange 2.
2536 if ((Start < End) || ((Start == End) && NonNewNum ==0)) {
2537 sub1_end = End;
2538 sub2_start = End;
2539 } else {
2540 sub1_end = MaxNum;
2541 sub2_start = 0;
2542 }
2543
2544 unsigned int current_time = WW3D::Get_Sync_Time();
2545
2546 // The following back-to-back pair of "for" loops traverses the circular
2547 // buffer subranges in proper order.
2548 unsigned int ckey = NumColorKeyFrames - 1;
2549 unsigned int akey = NumAlphaKeyFrames - 1;
2550 unsigned int skey = NumSizeKeyFrames - 1;
2551 unsigned int rkey = NumRotationKeyFrames - 1;
2552 unsigned int fkey = NumFrameKeyFrames - 1;
2553 unsigned int bkey = NumBlurTimeKeyFrames -1;
2554
2555 unsigned int part;
2556 Vector3 *color = Color ? Color->Get_Array(): NULL;
2557 float *alpha = Alpha ? Alpha->Get_Array(): NULL;
2558 float *size = Size ? Size->Get_Array(): NULL;
2559 uint8 *orientation = Orientation ? Orientation->Get_Array(): NULL;
2560 uint8 *frame = Frame ? Frame->Get_Array(): NULL;
2561 float *ucoord = UCoord ? UCoord->Get_Array() : NULL;
2562 Vector3 *tailposition = TailPosition ? TailPosition->Get_Array() : NULL;
2563
2564 Vector3 *position=NULL;
2565
2566 if (PingPongPosition) {
2567 int pingpong = WW3D::Get_Frame_Count() & 0x1;
2568 position = Position[pingpong]->Get_Array();
2569 } else {
2570 position = Position[0]->Get_Array();
2571 }
2572
2573
2574 for (part = Start; part < sub1_end; part++) {
2575
2576 unsigned int part_age = current_time - TimeStamp[part];
2577
2578 // Ensure the current color keyframe is correct, and calculate color state
2579 if (color) {
2580 // We go from older to younger particles, so we go backwards from the last keyframe until
2581 // age >= keytime. This loop must terminate because the 0th keytime is 0.
2582 for (; part_age < ColorKeyFrameTimes[ckey]; ckey--);
2583
2584 color[part] = ColorKeyFrameValues[ckey] +
2585 ColorKeyFrameDeltas[ckey] * (float)(part_age - ColorKeyFrameTimes[ckey]) +
2587 }
2588
2589 // Ensure the current alpha keyframe is correct, and calculate alpha state
2590 if (alpha) {
2591 // We go from older to younger particles, so we go backwards from the last keyframe until
2592 // age >= keytime. This loop must terminate because the 0th keytime is 0.
2593 for (; part_age < AlphaKeyFrameTimes[akey]; akey--);
2594
2595 alpha[part] = AlphaKeyFrameValues[akey] +
2596 AlphaKeyFrameDeltas[akey] * (float)(part_age - AlphaKeyFrameTimes[akey]) +
2598 }
2599
2600 // Ensure the current size keyframe is correct, and calculate size state
2601 if (size) {
2602 // We go from older to younger particles, so we go backwards from the last keyframe until
2603 // age >= keytime. This loop must terminate because the 0th keytime is 0.
2604 for (; part_age < SizeKeyFrameTimes[skey]; skey--);
2605
2606 size[part] = SizeKeyFrameValues[skey] +
2607 SizeKeyFrameDeltas[skey] * (float)(part_age - SizeKeyFrameTimes[skey]) +
2609
2610 // Size (unlike color and alpha) isn't clamped in the engine, so we need to clamp
2611 // negative values to zero here:
2612 size[part] = (size[part] >= 0.0f) ? size[part] : 0.0f;
2613 }
2614
2615 // Ensure the current rotation keyframe is correct, and calculate orientation state
2616 if (orientation) {
2617 // We go from older to younger particles, so we go backwards from the last keyframe until
2618 // age >= keytime. This loop must terminate because the 0th keytime is 0.
2619 for (; part_age < RotationKeyFrameTimes[rkey]; rkey--);
2620
2621 float f_delta_t = (float)(part_age - RotationKeyFrameTimes[rkey]);
2622 float tmp_orient = OrientationKeyFrameValues[rkey] +
2623 (RotationKeyFrameValues[rkey] + HalfRotationKeyFrameDeltas[rkey] * f_delta_t) * f_delta_t +
2624 RandomRotationEntries[part & NumRandomRotationEntriesMinus1] * (float)part_age +
2626
2627 orientation[part] = (uint)(((int)(tmp_orient * 256.0f)) & 0xFF);
2628 }
2629
2630 // Ensure the current frame keyframe is correct, and calculate frame state
2631 if (frame) {
2632 // Frame and ucoord are mutually exclusive
2633 WWASSERT(ucoord==NULL);
2634 // We go from older to younger particles, so we go backwards from the last keyframe until
2635 // age >= keytime. This loop must terminate because the 0th keytime is 0.
2636 for (; part_age < FrameKeyFrameTimes[fkey]; fkey--);
2637
2638 float tmp_frame = FrameKeyFrameValues[fkey] +
2639 FrameKeyFrameDeltas[fkey] * (float)(part_age - FrameKeyFrameTimes[fkey]) +
2641
2642 frame[part] = (uint)(((int)(tmp_frame)) & 0xFF);
2643 }
2644
2645 // Ensure the current frame keyframe is correct, and calculate frame state
2646 // ucoord is the same as frame but in float
2647 if (ucoord) {
2648 // Frame and ucoord are mutually exclusive
2649 WWASSERT(frame==NULL);
2650 // We go from older to younger particles, so we go backwards from the last keyframe until
2651 // age >= keytime. This loop must terminate because the 0th keytime is 0.
2652 for (; part_age < FrameKeyFrameTimes[fkey]; fkey--);
2653
2654 ucoord[part] = FrameKeyFrameValues[fkey] +
2655 FrameKeyFrameDeltas[fkey] * (float)(part_age - FrameKeyFrameTimes[fkey]) +
2657 }
2658
2659 if (tailposition) {
2660 // We go from older to younger particles, so we go backwards from the last keyframe until
2661 // age >= keytime. This loop must terminate because the 0th keytime is 0.
2662 float blur_time = BlurTimeKeyFrameValues[0];
2664 for (; part_age < BlurTimeKeyFrameTimes[bkey]; bkey--);
2665 blur_time = BlurTimeKeyFrameValues[bkey] +
2666 BlurTimeKeyFrameDeltas[bkey] * (float)(part_age - BlurTimeKeyFrameTimes[bkey]) +
2668 }
2669 tailposition[part]=position[part]-Velocity[part]*blur_time*1000;
2670 }
2671 }
2672
2673 for (part = sub2_start; part < End; part++) {
2674
2675 unsigned int part_age = current_time - TimeStamp[part];
2676
2677 // Ensure the current color keyframe is correct, and calculate color state
2678 if (color) {
2679 // We go from older to younger particles, so we go backwards from the last keyframe until
2680 // age >= keytime. This loop must terminate because the 0th keytime is 0.
2681 for (; part_age < ColorKeyFrameTimes[ckey]; ckey--);
2682
2683 color[part] =
2684 ColorKeyFrameValues[ckey] +
2685 ColorKeyFrameDeltas[ckey] * (float)(part_age - ColorKeyFrameTimes[ckey]) +
2687 }
2688
2689 // Ensure the current alpha keyframe is correct, and calculate alpha state
2690 if (alpha) {
2691 // We go from older to younger particles, so we go backwards from the last keyframe until
2692 // age >= keytime. This loop must terminate because the 0th keytime is 0.
2693 for (; part_age < AlphaKeyFrameTimes[akey]; akey--);
2694
2695 alpha[part] = AlphaKeyFrameValues[akey] +
2696 AlphaKeyFrameDeltas[akey] * (float)(part_age - AlphaKeyFrameTimes[akey]) +
2698 }
2699
2700 // Ensure the current size keyframe is correct, and calculate size state
2701 if (size) {
2702 // We go from older to younger particles, so we go backwards from the last keyframe until
2703 // age >= keytime. This loop must terminate because the 0th keytime is 0.
2704 for (; part_age < SizeKeyFrameTimes[skey]; skey--);
2705
2706 size[part] = SizeKeyFrameValues[skey] +
2707 SizeKeyFrameDeltas[skey] * (float)(part_age - SizeKeyFrameTimes[skey]) +
2709
2710 // Size (unlike color) isn't clamped in the engine, so we need to
2711 // clamp negative values to zero here:
2712 size[part] = (size[part] >= 0.0f) ? size[part] : 0.0f;
2713 }
2714
2715 // Ensure the current rotation keyframe is correct, and calculate orientation state
2716 if (orientation) {
2717 // We go from older to younger particles, so we go backwards from the last keyframe until
2718 // age >= keytime. This loop must terminate because the 0th keytime is 0.
2719 for (; part_age < RotationKeyFrameTimes[rkey]; rkey--);
2720
2721 float f_delta_t = (float)(part_age - RotationKeyFrameTimes[rkey]);
2722 float tmp_orient = OrientationKeyFrameValues[rkey] +
2723 (RotationKeyFrameValues[rkey] + HalfRotationKeyFrameDeltas[rkey] * f_delta_t) * f_delta_t +
2724 RandomRotationEntries[part & NumRandomRotationEntriesMinus1] * (float)part_age +
2726
2727 orientation[part] = (uint)(((int)(tmp_orient * 256.0f)) & 0xFF);
2728 }
2729
2730 // Ensure the current frame keyframe is correct, and calculate frame state
2731 if (frame) {
2732 // Frame and ucoord are mutually exclusive
2733 WWASSERT(ucoord==NULL);
2734 // We go from older to younger particles, so we go backwards from the last keyframe until
2735 // age >= keytime. This loop must terminate because the 0th keytime is 0.
2736 for (; part_age < FrameKeyFrameTimes[fkey]; fkey--);
2737
2738 float tmp_frame = FrameKeyFrameValues[fkey] +
2739 FrameKeyFrameDeltas[fkey] * (float)(part_age - FrameKeyFrameTimes[fkey]) +
2741
2742 frame[part] = (uint)(((int)(tmp_frame)) & 0xFF);
2743 }
2744
2745 // Ensure the current frame keyframe is correct, and calculate frame state
2746 // ucoord is the same as frame but in float
2747 if (ucoord) {
2748 // Frame and ucoord are mutually exclusive
2749 WWASSERT(frame==NULL);
2750 // We go from older to younger particles, so we go backwards from the last keyframe until
2751 // age >= keytime. This loop must terminate because the 0th keytime is 0.
2752 for (; part_age < FrameKeyFrameTimes[fkey]; fkey--);
2753
2754 ucoord[part] = FrameKeyFrameValues[fkey] +
2755 FrameKeyFrameDeltas[fkey] * (float)(part_age - FrameKeyFrameTimes[fkey]) +
2757 }
2758
2759 if (tailposition) {
2760 // We go from older to younger particles, so we go backwards from the last keyframe until
2761 // age >= keytime. This loop must terminate because the 0th keytime is 0.
2762 float blur_time = BlurTimeKeyFrameValues[0];
2764 for (; part_age < BlurTimeKeyFrameTimes[bkey]; bkey--);
2765 blur_time = BlurTimeKeyFrameValues[bkey] +
2766 BlurTimeKeyFrameDeltas[bkey] * (float)(part_age - BlurTimeKeyFrameTimes[bkey]) +
2768 }
2769 tailposition[part]=position[part]-Velocity[part]*blur_time*1000;
2770 }
2771 }
2772}
2773
2774
2776{
2777 // Ensure all particle positions are updated. If bounding box still not
2778 // dirty, return.
2780 if (!BoundingBoxDirty) return;
2781
2782 // If there are no particles, generate a dummy bounding box:
2783 if (NonNewNum == 0U) {
2784 BoundingBox.Init(Vector3(0.0, 0.0, 0.0), Vector3(0.0, 0.0, 0.0));
2785 BoundingBoxDirty = false;
2786 return;
2787 }
2788
2789 // Find min/max coord values for all points:
2790 int pingpong = 0;
2791 if (PingPongPosition) {
2792 pingpong = WW3D::Get_Frame_Count() & 0x1;
2793 }
2794 Vector3 *position = Position[pingpong]->Get_Array();
2795 Vector3 max_coords = position[Start];
2796 Vector3 min_coords = position[Start];
2797
2798 // In the general case, a range in a circular buffer can be composed of up
2799 // to two subranges. Find the Start - End subranges.
2800 unsigned int sub1_end; // End of subrange 1.
2801 unsigned int sub2_start; // Start of subrange 2.
2802 unsigned int i; // Loop index.
2803 if ((Start < End) || ((Start == End) && NonNewNum ==0)) {
2804 sub1_end = End;
2805 sub2_start = End;
2806 } else {
2807 sub1_end = MaxNum;
2808 sub2_start = 0;
2809 }
2810 for (i = Start; i < sub1_end; i++) {
2811 max_coords.X = max_coords.X >= position[i].X ? max_coords.X : position[i].X;
2812 max_coords.Y = max_coords.Y >= position[i].Y ? max_coords.Y : position[i].Y;
2813 max_coords.Z = max_coords.Z >= position[i].Z ? max_coords.Z : position[i].Z;
2814 min_coords.X = min_coords.X <= position[i].X ? min_coords.X : position[i].X;
2815 min_coords.Y = min_coords.Y <= position[i].Y ? min_coords.Y : position[i].Y;
2816 min_coords.Z = min_coords.Z <= position[i].Z ? min_coords.Z : position[i].Z;
2817 }
2818 for (i = sub2_start; i < End; i++) {
2819 max_coords.X = max_coords.X >= position[i].X ? max_coords.X : position[i].X;
2820 max_coords.Y = max_coords.Y >= position[i].Y ? max_coords.Y : position[i].Y;
2821 max_coords.Z = max_coords.Z >= position[i].Z ? max_coords.Z : position[i].Z;
2822 min_coords.X = min_coords.X <= position[i].X ? min_coords.X : position[i].X;
2823 min_coords.Y = min_coords.Y <= position[i].Y ? min_coords.Y : position[i].Y;
2824 min_coords.Z = min_coords.Z <= position[i].Z ? min_coords.Z : position[i].Z;
2825 }
2826
2827 // Extend by maximum possible particle size:
2829 max_coords += size;
2830 min_coords -= size;
2831
2832 // Update bounding box:
2833 BoundingBox.Init(MinMaxAABoxClass(min_coords,max_coords));
2834 BoundingBoxDirty = false;
2835}
2836
2837
2838// NOTE: typically, the number of new particles created in a frame is small
2839// relative to the total number of particles, so this is not the most
2840// performance-critical particle function. New particles are copied from the
2841// new particle vector into the circular buffer, overwriting any older
2842// particles (including possibly other new particles) so that the newest
2843// particles are preserved. The particles are initialized to their state at
2844// the end of the current interval.
2846{
2847 unsigned int current_time = WW3D::Get_Sync_Time();
2848
2849 // position is the current frame position, prev_pos is the previous frames position (only if
2850 // we have enabled pingpong position buffers)
2851 Vector3 *position;
2852 Vector3 *prev_pos;
2853 if (PingPongPosition) {
2854 int pingpong = WW3D::Get_Frame_Count() & 0x1;
2855 position = Position[pingpong]->Get_Array();
2856 prev_pos = Position[pingpong ^ 0x1]->Get_Array();
2857 } else {
2858 position = Position[0]->Get_Array();
2859 prev_pos = NULL;
2860 }
2861
2862 unsigned char * ids = GroupID->Get_Array();
2863 for (; NewParticleQueueCount;) {
2864
2865 // Get particle off new particle queue:
2869
2870 // Get particle birth time stamp, calculate age. If not under maxage
2871 // skip this particle.
2872 TimeStamp[NewEnd] = new_particle.TimeStamp;
2873 unsigned int age = current_time - TimeStamp[NewEnd];
2874 if (age >= MaxAge) continue;
2875 float fp_age = (float)age;
2876
2877 // Apply velocity and acceleration if present. Otherwise, just apply
2878 // velocity.
2879 if (HasAccel) {
2880 position[NewEnd] = new_particle.Position +
2881 (new_particle.Velocity + 0.5f * Accel * fp_age) * fp_age;
2882
2883 Velocity[NewEnd] = new_particle.Velocity + (Accel * fp_age);
2884 } else {
2885 position[NewEnd] =new_particle.Position +
2886 (new_particle.Velocity * fp_age);
2887 Velocity[NewEnd] = new_particle.Velocity;
2888 }
2889
2890 // If pingpong enabled, store starting position in prev_pos[].
2891 if (PingPongPosition) {
2892 prev_pos[NewEnd] = new_particle.Position;
2893 }
2894
2895 // upate the group id
2896 ids[NewEnd] = new_particle.GroupID;
2897
2898 // Advance the 'end of new particles' index.
2899 NewEnd++;
2900 if (NewEnd == MaxNum) NewEnd = 0;
2901
2902 // Update the new particles count.
2903 NewNum++;
2904
2905 // If we have just overflowed the total buffer, advance Start.
2906 if ((NewNum + NonNewNum) == (signed)(MaxNum + 1)) {
2907 Start++;
2908 if (Start == MaxNum) Start = 0;
2909 NonNewNum--;
2910
2911 // If this underflows the 'non-new' buffer, advance End.
2912 if (NonNewNum == -1) {
2913 End++;
2914 if (End == MaxNum) End = 0;
2915 NonNewNum = 0;
2916 NewNum--;
2917 }
2918 }
2919 }
2920}
2921
2922
2924{
2925 // Scan from Start and find the first particle which has an age less than
2926 // MaxAge - set Start to that position.
2927
2928 // In the general case, a range in a circular buffer can be composed of up
2929 // to two subranges. Find the Start - End subranges.
2930 unsigned int sub1_end; // End of subrange 1.
2931 unsigned int sub2_start; // Start of subrange 2.
2932 unsigned int i; // Loop index.
2933 if ((Start < End) || ((Start == End) && NonNewNum ==0)) {
2934 sub1_end = End;
2935 sub2_start = End;
2936 } else {
2937 sub1_end = MaxNum;
2938 sub2_start = 0;
2939 }
2940
2941 unsigned int current_time = WW3D::Get_Sync_Time();
2942
2943 // Stop when the current particle is young enough to be alive.
2944 bool broke = false;
2945 for (i = Start; i < sub1_end; i++) {
2946 if ((current_time - TimeStamp[i]) < MaxAge) {
2947 broke = true;
2948 break;
2949 }
2950 NonNewNum--;
2951 }
2952 if (!broke) {
2953 for (i = sub2_start; i < End; i++) {
2954 if ((current_time - TimeStamp[i]) < MaxAge) break;
2955 NonNewNum--;
2956 }
2957 }
2958
2959 Start = i;
2960
2961 // NOTE: we do not scan the new particles, because they have been already
2962 // preculled to be under MaxAge.
2963}
2964
2965
2967{
2968 // In the general case, a range in a circular buffer can be composed of up
2969 // to two subranges. Find the Start - End subranges.
2970 unsigned int sub1_end; // End of subrange 1.
2971 unsigned int sub2_start; // Start of subrange 2.
2972 unsigned int i; // Loop index.
2973 if ((Start < End) || ((Start == End) && NonNewNum ==0)) {
2974 sub1_end = End;
2975 sub2_start = End;
2976 } else {
2977 sub1_end = MaxNum;
2978 sub2_start = 0;
2979 }
2980
2981 float fp_elapsed_time = (float)elapsed;
2982
2983 // Update position and velocity for all particles.
2984 if (PingPongPosition) {
2985
2986 int pingpong = WW3D::Get_Frame_Count() & 0x1;
2987 Vector3 *position = Position[pingpong]->Get_Array();
2988 Vector3 *prev_pos = Position[pingpong ^ 0x1]->Get_Array();
2989
2990 if (HasAccel) {
2991 Vector3 delta_v = Accel * fp_elapsed_time;
2992 Vector3 accel_p = Accel * (0.5f * fp_elapsed_time * fp_elapsed_time);
2993 for (i = Start; i < sub1_end; i++) {
2994 position[i] = prev_pos[i] + Velocity[i] * fp_elapsed_time + accel_p;
2995 Velocity[i] += delta_v;
2996 }
2997 for (i = sub2_start; i < End; i++) {
2998 position[i] = prev_pos[i] + Velocity[i] * fp_elapsed_time + accel_p;
2999 Velocity[i] += delta_v;
3000 }
3001 } else {
3002 for (i = Start; i < sub1_end; i++) {
3003 position[i] += Velocity[i] * fp_elapsed_time;
3004 }
3005 for (i = sub2_start; i < End; i++) {
3006 position[i] += Velocity[i] * fp_elapsed_time;
3007 }
3008 }
3009 } else {
3010
3011 Vector3 *position = Position[0]->Get_Array();
3012
3013 if (HasAccel) {
3014 Vector3 delta_v = Accel * fp_elapsed_time;
3015 Vector3 accel_p = Accel * (0.5f * fp_elapsed_time * fp_elapsed_time);
3016 for (i = Start; i < sub1_end; i++) {
3017 position[i] += Velocity[i] * fp_elapsed_time + accel_p;
3018 Velocity[i] += delta_v;
3019 }
3020 for (i = sub2_start; i < End; i++) {
3021 position[i] += Velocity[i] * fp_elapsed_time + accel_p;
3022 Velocity[i] += delta_v;
3023 }
3024 } else {
3025 for (i = Start; i < sub1_end; i++) {
3026 position[i] += Velocity[i] * fp_elapsed_time;
3027 }
3028 for (i = sub2_start; i < End; i++) {
3029 position[i] += Velocity[i] * fp_elapsed_time;
3030 }
3031 }
3032 }
3033}
3034
3036{
3037 int real_keyframe_count = (NumColorKeyFrames > 0) ? (NumColorKeyFrames - 1) : 0;
3038 bool create_last_keyframe = false;
3039
3040 //
3041 // Determine if there is a keyframe at the very end of the particle's lifetime
3042 //
3043 if ((ColorKeyFrameDeltas != NULL) &&
3044 ((ColorKeyFrameDeltas[NumColorKeyFrames - 1].X != 0) ||
3045 (ColorKeyFrameDeltas[NumColorKeyFrames - 1].Y != 0) ||
3046 (ColorKeyFrameDeltas[NumColorKeyFrames - 1].Z != 0))) {
3047 real_keyframe_count ++;
3048 create_last_keyframe = true;
3049 }
3050
3051 colors.Start = ColorKeyFrameValues[0];
3052 colors.Rand = ColorRandom;
3053 colors.NumKeyFrames = real_keyframe_count;
3054 colors.KeyTimes = NULL;
3055 colors.Values = NULL;
3056
3057 //
3058 // If we have more than just the start color, build
3059 // an array of key times and color vatues
3060 //
3061 if (real_keyframe_count > 0) {
3062 colors.KeyTimes = W3DNEWARRAY float[real_keyframe_count];
3063 colors.Values = W3DNEWARRAY Vector3[real_keyframe_count];
3064
3065 //
3066 // Copy the keytimes and color values
3067 //
3068 unsigned int index;
3069 for (index = 1; index < NumColorKeyFrames; index ++) {
3070 colors.KeyTimes[index - 1] = ((float)ColorKeyFrameTimes[index]) / 1000;
3071 colors.Values[index - 1] = ColorKeyFrameValues[index];
3072 }
3073
3074 //
3075 // Add a keyframe at the very end of the timeline if necessary
3076 //
3077 if (create_last_keyframe) {
3078 colors.KeyTimes[index - 1] = ((float)MaxAge / 1000);
3079
3080 //
3081 // Determine what the value of the last keyframe should be
3082 //
3083 Vector3 start_color = ColorKeyFrameValues[index - 1];
3085 float time_delta = MaxAge - ColorKeyFrameTimes[index - 1];
3086 colors.Values[index - 1] = start_color + (delta * time_delta);
3087 }
3088 }
3089
3090 return ;
3091}
3092
3094{
3095 int real_keyframe_count = (NumAlphaKeyFrames > 0) ? (NumAlphaKeyFrames - 1) : 0;
3096 bool create_last_keyframe = false;
3097
3098 //
3099 // Determine if there is a keyframe at the very end of the particle's lifetime
3100 //
3101 if ((AlphaKeyFrameDeltas != NULL) &&
3103 real_keyframe_count ++;
3104 create_last_keyframe = true;
3105 }
3106
3107 opacities.Start = AlphaKeyFrameValues[0];
3108 opacities.Rand = OpacityRandom;
3109 opacities.NumKeyFrames = real_keyframe_count;
3110 opacities.KeyTimes = NULL;
3111 opacities.Values = NULL;
3112
3113 //
3114 // If we have more than just the start opacity, build
3115 // an array of key times and opacity values
3116 //
3117 if (real_keyframe_count > 0) {
3118 opacities.KeyTimes = W3DNEWARRAY float[real_keyframe_count];
3119 opacities.Values = W3DNEWARRAY float[real_keyframe_count];
3120
3121 //
3122 // Copy the keytimes and opacity values
3123 //
3124 unsigned int index;
3125 for (index = 1; index < NumAlphaKeyFrames; index ++) {
3126 opacities.KeyTimes[index - 1] = ((float)AlphaKeyFrameTimes[index]) / 1000;
3127 opacities.Values[index - 1] = AlphaKeyFrameValues[index];
3128 }
3129
3130 //
3131 // Add a keyframe at the very end of the timeline if necessary
3132 //
3133 if (create_last_keyframe) {
3134 opacities.KeyTimes[index - 1] = ((float)MaxAge / 1000);
3135
3136 //
3137 // Determine what the value of the last keyframe should be
3138 //
3139 float start_alpha = AlphaKeyFrameValues[index - 1];
3140 float &delta = AlphaKeyFrameDeltas[NumAlphaKeyFrames - 1];
3141 float time_delta = MaxAge - AlphaKeyFrameTimes[index - 1];
3142 opacities.Values[index - 1] = start_alpha + (delta * time_delta);
3143 }
3144 }
3145
3146 return ;
3147}
3148
3149
3151{
3152 int real_keyframe_count = (NumSizeKeyFrames > 0) ? (NumSizeKeyFrames - 1) : 0;
3153 bool create_last_keyframe = false;
3154
3155 //
3156 // Determine if there is a keyframe at the very end of the particle's lifetime
3157 //
3158 if ((SizeKeyFrameDeltas != NULL) &&
3160 real_keyframe_count ++;
3161 create_last_keyframe = true;
3162 }
3163
3164 sizes.Start = SizeKeyFrameValues[0];
3165 sizes.Rand = SizeRandom;
3166 sizes.NumKeyFrames = real_keyframe_count;
3167 sizes.KeyTimes = NULL;
3168 sizes.Values = NULL;
3169
3170 //
3171 // If we have more than just the start opacity, build
3172 // an array of key times and opacity values
3173 //
3174 if (real_keyframe_count > 0) {
3175 sizes.KeyTimes = W3DNEWARRAY float[real_keyframe_count];
3176 sizes.Values = W3DNEWARRAY float[real_keyframe_count];
3177
3178 //
3179 // Copy the keytimes and size values
3180 //
3181 unsigned int index;
3182 for (index = 1; index < NumSizeKeyFrames; index ++) {
3183 sizes.KeyTimes[index - 1] = ((float)SizeKeyFrameTimes[index]) / 1000;
3184 sizes.Values[index - 1] = SizeKeyFrameValues[index];
3185 }
3186
3187 //
3188 // Add a keyframe at the very end of the timeline if necessary
3189 //
3190 if (create_last_keyframe) {
3191 sizes.KeyTimes[index - 1] = ((float)MaxAge / 1000);
3192
3193 //
3194 // Determine what the value of the last keyframe should be
3195 //
3196 float start_size = SizeKeyFrameValues[index - 1];
3197 float &delta = SizeKeyFrameDeltas[NumSizeKeyFrames - 1];
3198 float time_delta = MaxAge - SizeKeyFrameTimes[index - 1];
3199 sizes.Values[index - 1] = start_size + (delta * time_delta);
3200 }
3201 }
3202
3203 return ;
3204}
3205
3206
3208{
3209 int real_keyframe_count = (NumRotationKeyFrames > 0) ? (NumRotationKeyFrames - 1) : 0;
3210 bool create_last_keyframe = false;
3211
3212 /*
3213 ** NOTE: Rotations are stored internally in rotations per millisecond. These will be converted to rotations per second.
3214 */
3215
3216 //
3217 // Determine if there is a keyframe at the very end of the particle's lifetime
3218 //
3221 real_keyframe_count ++;
3222 create_last_keyframe = true;
3223 }
3224
3225 // Convert the rotation values from rotations per millisecond to rotations per second.
3226 rotations.Start = RotationKeyFrameValues ? RotationKeyFrameValues[0] * 1000.0f : 0;
3227 rotations.Rand = RotationRandom * 1000.0f;
3228 rotations.NumKeyFrames = real_keyframe_count;
3229 rotations.KeyTimes = NULL;
3230 rotations.Values = NULL;
3231
3232 //
3233 // If we have more than just the start rotation, build
3234 // an array of key times and rotation values
3235 //
3236 if (real_keyframe_count > 0) {
3237 rotations.KeyTimes = W3DNEWARRAY float[real_keyframe_count];
3238 rotations.Values = W3DNEWARRAY float[real_keyframe_count];
3239
3240 //
3241 // Copy the keytimes and rotation values
3242 //
3243 unsigned int index;
3244 for (index = 1; index < NumRotationKeyFrames; index ++) {
3245 rotations.KeyTimes[index - 1] = ((float)RotationKeyFrameTimes[index]) / 1000;
3246 rotations.Values[index - 1] = RotationKeyFrameValues[index] * 1000.0f;
3247 }
3248
3249 //
3250 // Add a keyframe at the very end of the timeline if necessary
3251 //
3252 if (create_last_keyframe) {
3253 rotations.KeyTimes[index - 1] = ((float)MaxAge / 1000);
3254
3255 //
3256 // Determine what the value of the last keyframe should be
3257 //
3258 float start_rotation = RotationKeyFrameValues[index - 1];
3259 float delta = 2.0f * HalfRotationKeyFrameDeltas[NumRotationKeyFrames - 1];
3260 float time_delta = MaxAge - RotationKeyFrameTimes[index - 1];
3261 rotations.Values[index - 1] = (start_rotation + (delta * time_delta)) * 1000.0f;
3262 }
3263 }
3264
3265 return ;
3266}
3267
3268
3270{
3271 int real_keyframe_count = (NumFrameKeyFrames > 0) ? (NumFrameKeyFrames - 1) : 0;
3272 bool create_last_keyframe = false;
3273
3274 //
3275 // Determine if there is a keyframe at the very end of the particle's lifetime
3276 //
3277 if ((FrameKeyFrameDeltas != NULL) &&
3279 real_keyframe_count ++;
3280 create_last_keyframe = true;
3281 }
3282
3283 frames.Start = FrameKeyFrameValues[0];
3284 frames.Rand = FrameRandom;
3285 frames.NumKeyFrames = real_keyframe_count;
3286 frames.KeyTimes = NULL;
3287 frames.Values = NULL;
3288
3289 //
3290 // If we have more than just the start rotation, build
3291 // an array of key times and frame values
3292 //
3293 if (real_keyframe_count > 0) {
3294 frames.KeyTimes = W3DNEWARRAY float[real_keyframe_count];
3295 frames.Values = W3DNEWARRAY float[real_keyframe_count];
3296
3297 //
3298 // Copy the keytimes and frame values
3299 //
3300 unsigned int index;
3301 for (index = 1; index < NumFrameKeyFrames; index ++) {
3302 frames.KeyTimes[index - 1] = ((float)FrameKeyFrameTimes[index]) / 1000;
3303 frames.Values[index - 1] = FrameKeyFrameValues[index];
3304 }
3305
3306 //
3307 // Add a keyframe at the very end of the timeline if necessary
3308 //
3309 if (create_last_keyframe) {
3310 frames.KeyTimes[index - 1] = ((float)MaxAge / 1000);
3311
3312 //
3313 // Determine what the value of the last keyframe should be
3314 //
3315 float start_frame = FrameKeyFrameValues[index - 1];
3316 float &delta = FrameKeyFrameDeltas[NumFrameKeyFrames - 1];
3317 float time_delta = MaxAge - FrameKeyFrameTimes[index - 1];
3318 frames.Values[index - 1] = start_frame + (delta * time_delta);
3319 }
3320 }
3321
3322 return ;
3323}
3324
3326{
3327 int real_keyframe_count = (NumBlurTimeKeyFrames > 0) ? (NumBlurTimeKeyFrames - 1) : 0;
3328 bool create_last_keyframe = false;
3329
3330 //
3331 // Determine if there is a keyframe at the very end of the particle's lifetime
3332 //
3333 if ((BlurTimeKeyFrameDeltas != NULL) &&
3335 real_keyframe_count ++;
3336 create_last_keyframe = true;
3337 }
3338
3339 blurtimes.Start = BlurTimeKeyFrameValues[0];
3340 blurtimes.Rand = BlurTimeRandom;
3341 blurtimes.NumKeyFrames = real_keyframe_count;
3342 blurtimes.KeyTimes = NULL;
3343 blurtimes.Values = NULL;
3344
3345 //
3346 // If we have more than just the start rotation, build
3347 // an array of key times and blur time values
3348 //
3349 if (real_keyframe_count > 0) {
3350 blurtimes.KeyTimes = new float[real_keyframe_count];
3351 blurtimes.Values = new float[real_keyframe_count];
3352
3353 //
3354 // Copy the keytimes and frame values
3355 //
3356 unsigned int index;
3357 for (index = 1; index < NumBlurTimeKeyFrames; index ++) {
3358 blurtimes.KeyTimes[index - 1] = ((float)BlurTimeKeyFrameTimes[index]) / 1000;
3359 blurtimes.Values[index - 1] = BlurTimeKeyFrameValues[index];
3360 }
3361
3362 //
3363 // Add a keyframe at the very end of the timeline if necessary
3364 //
3365 if (create_last_keyframe) {
3366 blurtimes.KeyTimes[index - 1] = ((float)MaxAge / 1000);
3367
3368 //
3369 // Determine what the value of the last keyframe should be
3370 //
3371 float start_blurtime = BlurTimeKeyFrameValues[index - 1];
3372 float &delta = BlurTimeKeyFrameDeltas[NumBlurTimeKeyFrames - 1];
3373 float time_delta = MaxAge - BlurTimeKeyFrameTimes[index - 1];
3374 blurtimes.Values[index - 1] = start_blurtime + (delta * time_delta);
3375 }
3376 }
3377
3378 return ;
3379}
3380
3381void ParticleBufferClass::Set_LOD_Max_Screen_Size(int lod_level,float max_screen_size)
3382{
3383 if ((lod_level <0) || (lod_level > 17)) {
3384 return;
3385 }
3386 LODMaxScreenSizes[lod_level] = max_screen_size;
3387}
3388
3389
3391{
3392 if ((lod_level <0) || (lod_level > 17)) {
3393 return NO_MAX_SCREEN_SIZE;
3394 }
3395 return LODMaxScreenSizes[lod_level];
3396}
3397
3398
3400{
3401 if (LineRenderer != NULL) {
3402 return LineRenderer->Get_Texture_Mapping_Mode();
3403 }
3405}
3406
3408{
3409 if (LineRenderer != NULL) {
3410 return LineRenderer->Is_Merge_Intersections();
3411 }
3412 return false;
3413}
3414
3416{
3417 if (LineRenderer != NULL) {
3418 return LineRenderer->Is_Freeze_Random();
3419 }
3420 return false;
3421}
3422
3424{
3425 if (LineRenderer != NULL) {
3426 return LineRenderer->Is_Sorting_Disabled();
3427 }
3428 return false;
3429}
3430
3432{
3433 if (LineRenderer != NULL) {
3434 return LineRenderer->Are_End_Caps_Enabled();
3435 }
3436 return false;
3437}
3438
3440{
3441 if (LineRenderer != NULL) {
3442 return LineRenderer->Get_Current_Subdivision_Level();
3443 }
3444 return 0;
3445}
3446
3448{
3449 if (LineRenderer != NULL) {
3450 return LineRenderer->Get_Noise_Amplitude();
3451 }
3452 return 0.0f;
3453}
3454
3456{
3457 if (LineRenderer != NULL) {
3458 return LineRenderer->Get_Merge_Abort_Factor();
3459 }
3460 return 0.0f;
3461}
3462
3464{
3465 if (LineRenderer != NULL) {
3466 return LineRenderer->Get_Texture_Tile_Factor();
3467 }
3468 return 1.0f;
3469}
3470
3472{
3473 if (LineRenderer != NULL) {
3474 return LineRenderer->Get_UV_Offset_Rate();
3475 }
3476 return Vector2(0.0f,0.0f);
3477}
3478
3480{
3481 // if there is a texture, the assumption is that the artist
3482 // is controlling the fadeoff ramp using the texture
3483 // thus, the ARGB of the tail should be the same as the head
3485 if (tex)
3486 {
3487 REF_PTR_RELEASE(tex);
3488 return SAME_AS_HEAD;
3489 }
3490
3491 ShaderClass shader=Get_Shader();
3492
3493
3494 //Multiplicative RGB is white (A is don't care)
3495 //Additive RGB is Black (A is don't care)
3496 //Screen RGB is Black (A is don't care)
3497 //Alpha Same RGB as head but A is 0
3498 //Alpha test blend Same ARGB as head but A is 0
3499 //Alpha test Same ARGB as head
3500 //Opaque Same ARGB as head
3501
3502 // Multiplicative
3504 // Additive
3506 // Screen
3508 // Alpha
3510 // Alpha test
3512
3513 return SAME_AS_HEAD;
3514}
3515
3517{
3518 if (PointGroup) return PointGroup->Get_Texture();
3519 else if (LineGroup) return LineGroup->Get_Texture();
3520 else if (LineRenderer) return LineRenderer->Get_Texture();
3521 return NULL;
3522}
3523
3525{
3526 if (PointGroup) PointGroup->Set_Texture(tex);
3527 else if (LineGroup) LineGroup->Set_Texture(tex);
3528 else if (LineRenderer) LineRenderer->Set_Texture(tex);
3529}
3530
3532{
3533 if (PointGroup) return PointGroup->Get_Shader();
3534 else if (LineGroup) return LineGroup->Get_Shader();
3535 else if (LineRenderer) return LineRenderer->Get_Shader();
3536
3537 WWASSERT(0);
3539}
#define NULL
Definition BaseType.h:92
Color scale(const Color &a, const Color &b)
Definition GameMtl.cpp:722
#define WWASSERT
#define W3D_EMITTER_RENDER_MODE_LINEGRP_TETRA
Definition w3d_file.h:1816
#define W3D_EMITTER_RENDER_MODE_LINE
Definition w3d_file.h:1815
#define W3D_EMITTER_RENDER_MODE_LINEGRP_PRISM
Definition w3d_file.h:1817
#define SORT_LEVEL_NONE
Definition w3d_file.h:1195
#define W3D_EMITTER_RENDER_MODE_QUAD_PARTICLES
Definition w3d_file.h:1814
#define W3D_EMITTER_RENDER_MODE_TRI_PARTICLES
Definition w3d_file.h:1813
#define NO_MAX_SCREEN_SIZE
Definition w3d_file.h:2045
#define W3DNEWARRAY
Definition always.h:110
#define MIN(a, b)
Definition always.h:189
#define W3DNEW
Definition always.h:109
#define MAX(a, b)
Definition always.h:185
unsigned int uint
Definition bittype.h:47
unsigned char uint8
Definition bittype.h:44
@ true
Definition bool.h:59
@ false
Definition bool.h:59
#define WWMATH_EPSILON
Definition wwmath.h:54
#define WWMATH_PI
Definition wwmath.h:56
int Find_POT(int val)
Definition pot.cpp:53
T Bound(T original, T minval, T maxval)
Definition bound.h:44
void Get_View_Plane(Vector2 &set_min, Vector2 &set_max) const
Definition camera.cpp:380
void Get_Viewport(Vector2 &set_min, Vector2 &set_max) const
Definition camera.h:281
TextureClass * Peek_Texture(void)
Definition linegrp.cpp:204
ShaderClass Get_Shader(void)
Definition linegrp.cpp:214
virtual bool Is_Complete(void)
Definition part_buf.h:170
void Reset_Blur_Times(ParticlePropertyStruct< float > &new_blur_times)
unsigned int * ColorKeyFrameTimes
Definition part_buf.h:328
ShaderClass Get_Shader(void) const
float * FrameKeyFrameDeltas
Definition part_buf.h:347
unsigned int NewEnd
Definition part_buf.h:308
virtual int Calculate_Cost_Value_Arrays(float screen_area, float *values, float *costs) const
void Update_Bounding_Box(void)
static float Get_LOD_Max_Screen_Size(int lod_level)
float * RandomRotationEntries
Definition part_buf.h:370
ParticleEmitterClass * Emitter
Definition part_buf.h:425
SegLineRendererClass * LineRenderer
Definition part_buf.h:390
void Get_Rotation_Key_Frames(ParticlePropertyStruct< float > &rotations) const
unsigned int * BlurTimeKeyFrameTimes
Definition part_buf.h:349
unsigned int DecimationThreshold
Definition part_buf.h:433
void Get_Frame_Key_Frames(ParticlePropertyStruct< float > &frames) const
int Is_Freeze_Random(void) const
virtual void Prepare_LOD(CameraClass &camera)
virtual void Notify_Removed(SceneClass *scene)
unsigned int * FrameKeyFrameTimes
Definition part_buf.h:345
ShareBufferClass< float > * Size
Definition part_buf.h:404
ShareBufferClass< uint8 > * Frame
Definition part_buf.h:405
virtual float Get_Cost(void) const
ShareBufferClass< float > * Alpha
Definition part_buf.h:403
unsigned int Start
Definition part_buf.h:306
virtual void Scale(float scale)
unsigned int NumFrameKeyFrames
Definition part_buf.h:344
unsigned int * RotationKeyFrameTimes
Definition part_buf.h:340
unsigned int NumRandomFrameEntriesMinus1
Definition part_buf.h:373
ShareBufferClass< unsigned char > * GroupID
Definition part_buf.h:411
unsigned char CurrentGroupID
Definition part_buf.h:464
float * BlurTimeKeyFrameValues
Definition part_buf.h:350
unsigned int NumRandomBlurTimeEntriesMinus1
Definition part_buf.h:375
virtual void Render(RenderInfoClass &rinfo)
Definition part_buf.cpp:815
unsigned int LastUpdateTime
Definition part_buf.h:295
virtual void Increment_LOD(void)
virtual int Get_LOD_Count(void) const
virtual ~ParticleBufferClass(void)
Definition part_buf.cpp:729
NewParticleStruct * Add_Uninitialized_New_Particle(void)
unsigned int NumSizeKeyFrames
Definition part_buf.h:335
TailDiffuseTypeEnum Determine_Tail_Diffuse()
ShareBufferClass< Vector3 > * TailPosition
Definition part_buf.h:407
void Render_Particles(RenderInfoClass &rinfo)
Definition part_buf.cpp:939
ParticleBufferClass(ParticleEmitterClass *emitter, unsigned int buffer_size, ParticlePropertyStruct< Vector3 > &color, ParticlePropertyStruct< float > &opacity, ParticlePropertyStruct< float > &size, ParticlePropertyStruct< float > &rotation, float orient_rnd, ParticlePropertyStruct< float > &frame, ParticlePropertyStruct< float > &blurtime, Vector3 accel, float max_age, float future_start, TextureClass *tex, ShaderClass shader, bool pingpong, int render_mode, int frame_mode, const W3dEmitterLinePropertiesStruct *line_props)
Definition part_buf.cpp:85
void Set_Texture(TextureClass *tex)
unsigned int LodCount
Definition part_buf.h:437
unsigned int FutureStartTime
Definition part_buf.h:294
float * RandomAlphaEntries
Definition part_buf.h:366
void Get_Size_Key_Frames(ParticlePropertyStruct< float > &sizes) const
void Generate_APT(ShareBufferClass< unsigned int > **apt, unsigned int &active_point_count)
Definition part_buf.cpp:856
void Update_Visual_Particle_State(void)
void Get_Opacity_Key_Frames(ParticlePropertyStruct< float > &opacities) const
float * AlphaKeyFrameValues
Definition part_buf.h:333
float Get_Merge_Abort_Factor(void) const
float * AlphaKeyFrameDeltas
Definition part_buf.h:334
float Get_Texture_Tile_Factor(void) const
ShareBufferClass< Vector4 > * Diffuse
Definition part_buf.h:401
LineGroupClass * LineGroup
Definition part_buf.h:393
ShareBufferClass< unsigned int > * APT
Definition part_buf.h:410
virtual RenderObjClass * Clone(void) const
Definition part_buf.cpp:798
void Combine_Color_And_Alpha()
Definition part_buf.cpp:897
float Get_Particle_Size(void) const
Definition part_buf.h:187
unsigned int NewParticleQueueStart
Definition part_buf.h:284
void Reset_Opacity(ParticlePropertyStruct< float > &new_props)
unsigned int NumBlurTimeKeyFrames
Definition part_buf.h:348
unsigned int NumRandomSizeEntriesMinus1
Definition part_buf.h:367
Vector3 * Velocity
Definition part_buf.h:418
unsigned int NewParticleQueueEnd
Definition part_buf.h:285
virtual void Update_Cached_Bounding_Volumes(void) const
ParticleBufferClass & operator=(const ParticleBufferClass &)
Definition part_buf.cpp:717
Vector3 * RandomColorEntries
Definition part_buf.h:364
unsigned int * AlphaKeyFrameTimes
Definition part_buf.h:332
float Get_Noise_Amplitude(void) const
void Get_Color_Key_Frames(ParticlePropertyStruct< Vector3 > &colors) const
float * OrientationKeyFrameValues
Definition part_buf.h:343
ShareBufferClass< Vector4 > * TailDiffuse
Definition part_buf.h:408
virtual void Get_Obj_Space_Bounding_Sphere(SphereClass &sphere) const
ShareBufferClass< Vector3 > * Color
Definition part_buf.h:402
void Reset_Rotations(ParticlePropertyStruct< float > &new_rotations, float orient_rnd)
float * BlurTimeKeyFrameDeltas
Definition part_buf.h:351
float InitialOrientationRandom
Definition part_buf.h:384
unsigned int MaxNum
Definition part_buf.h:305
float * RandomOrientationEntries
Definition part_buf.h:372
unsigned int NumRandomAlphaEntriesMinus1
Definition part_buf.h:365
Vector2 Get_UV_Offset_Rate(void) const
ShareBufferClass< uint8 > * Orientation
Definition part_buf.h:409
void Get_New_Particles(void)
int Get_Subdivision_Level(void) const
int Are_End_Caps_Enabled(void) const
virtual void Get_Obj_Space_Bounding_Box(AABoxClass &box) const
unsigned int * TimeStamp
Definition part_buf.h:419
unsigned int NumRandomOrientationEntriesMinus1
Definition part_buf.h:371
void Kill_Old_Particles(void)
Vector3 * ColorKeyFrameDeltas
Definition part_buf.h:330
float * RandomSizeEntries
Definition part_buf.h:368
ShareBufferClass< Vector3 > * Position[2]
Definition part_buf.h:400
TextureClass * Get_Texture(void) const
NewParticleStruct * NewParticleQueue
Definition part_buf.h:283
virtual float Get_Value(void) const
void Emitter_Is_Dead(void)
unsigned int MaxAge
Definition part_buf.h:293
void Update_Non_New_Particles(unsigned int elapsed)
unsigned int * SizeKeyFrameTimes
Definition part_buf.h:336
void Update_Kinematic_Particle_State(void)
AABoxClass BoundingBox
Definition part_buf.h:313
virtual void Decrement_LOD(void)
void Reset_Size(ParticlePropertyStruct< float > &new_props)
void Render_Line(RenderInfoClass &rinfo)
Definition part_buf.cpp:985
float * SizeKeyFrameValues
Definition part_buf.h:337
unsigned int NumRandomColorEntriesMinus1
Definition part_buf.h:363
unsigned int NumColorKeyFrames
Definition part_buf.h:327
float * RandomBlurTimeEntries
Definition part_buf.h:376
Vector4 DefaultTailDiffuse
Definition part_buf.h:353
unsigned int NumRotationKeyFrames
Definition part_buf.h:339
virtual int Get_LOD_Level(void) const
Vector3 * ColorKeyFrameValues
Definition part_buf.h:329
PointGroupClass * PointGroup
Definition part_buf.h:387
static unsigned int TotalActiveCount
Definition part_buf.h:446
static const unsigned int PermutationArray[16]
Definition part_buf.h:56
int Get_Particle_Count(void) const
Definition part_buf.cpp:810
float * RandomFrameEntries
Definition part_buf.h:374
unsigned int End
Definition part_buf.h:307
void Reset_Colors(ParticlePropertyStruct< Vector3 > &new_props)
int Get_Line_Texture_Mapping_Mode(void) const
int Is_Merge_Intersections(void) const
float * SizeKeyFrameDeltas
Definition part_buf.h:338
virtual void Set_LOD_Level(int lod)
virtual int Get_Num_Polys(void) const
Definition part_buf.cpp:804
ShareBufferClass< float > * UCoord
Definition part_buf.h:406
virtual float Get_Post_Increment_Value(void) const
void Reset_Frames(ParticlePropertyStruct< float > &new_frames)
int Is_Sorting_Disabled(void) const
float * RotationKeyFrameValues
Definition part_buf.h:341
float * HalfRotationKeyFrameDeltas
Definition part_buf.h:342
virtual void Notify_Added(SceneClass *scene)
void Render_Line_Group(RenderInfoClass &rinfo)
static float LODMaxScreenSizes[17]
Definition part_buf.h:69
static void Set_LOD_Max_Screen_Size(int lod_level, float max_screen_size)
unsigned int NumAlphaKeyFrames
Definition part_buf.h:331
float * FrameKeyFrameValues
Definition part_buf.h:346
unsigned int NumRandomRotationEntriesMinus1
Definition part_buf.h:369
void Set_Emitter(ParticleEmitterClass *emitter)
virtual void On_Frame_Update(void)
void Get_Blur_Time_Key_Frames(ParticlePropertyStruct< float > &blurtimes) const
ShaderClass Get_Shader(void)
Definition pointgr.cpp:657
unsigned char Get_Frame_Row_Column_Count_Log2(void)
Definition pointgr.cpp:709
TextureClass * Peek_Texture(void)
Definition pointgr.cpp:614
static void Add_Object(RenderObjClass *robj)
Definition predlod.cpp:226
Vector3 Get_Position(void) const
Definition rendobj.cpp:508
void Validate_Cached_Bounding_Volumes(void) const
Definition rendobj.h:524
virtual int Is_Not_Hidden_At_All(void)
Definition rendobj.h:463
RenderObjClass(void)
Definition rendobj.cpp:170
virtual const SphereClass & Get_Bounding_Sphere(void) const
Definition rendobj.h:567
AABoxClass CachedBoundingBox
Definition rendobj.h:553
virtual void Notify_Added(SceneClass *scene)
Definition rendobj.cpp:862
void Invalidate_Cached_Bounding_Volumes(void) const
Definition rendobj.h:523
RenderObjClass & operator=(const RenderObjClass &)
Definition rendobj.cpp:232
SceneClass * Scene
Definition rendobj.h:557
static const float AT_MAX_LOD
Definition rendobj.h:405
virtual void Set_Force_Visible(int onoff)
Definition rendobj.h:476
virtual void Notify_Removed(SceneClass *scene)
Definition rendobj.cpp:884
friend class SceneClass
Definition rendobj.h:563
Matrix3D Transform
Definition rendobj.h:549
SphereClass CachedBoundingSphere
Definition rendobj.h:552
static const float AT_MIN_LOD
Definition rendobj.h:404
virtual void Register(RenderObjClass *obj, RegType for_what)=0
@ ON_FRAME_UPDATE
Definition scene.h:165
@ RELEASE
Definition scene.h:167
virtual void Unregister(RenderObjClass *obj, RegType for_what)=0
static ShaderClass _PresetOpaqueShader
Definition shader.h:365
SrcBlendFuncType Get_Src_Blend_Func(void) const
Definition shader.h:319
void Enable_Fog(const char *source)
Definition shader.cpp:280
DstBlendFuncType Get_Dst_Blend_Func(void) const
Definition shader.h:315
@ SRCBLEND_SRC_ALPHA
Definition shader.h:212
@ SRCBLEND_ONE
Definition shader.h:211
int Guess_Sort_Level(void) const
Definition shader.cpp:1123
AlphaTestType Get_Alpha_Test(void) const
Definition shader.h:313
@ ALPHATEST_ENABLE
Definition shader.h:97
@ DSTBLEND_ONE_MINUS_SRC_COLOR
Definition shader.h:175
@ DSTBLEND_ONE_MINUS_SRC_ALPHA
Definition shader.h:177
@ DSTBLEND_SRC_COLOR
Definition shader.h:174
@ DSTBLEND_ONE
Definition shader.h:173
void Delete_All(bool allow_shrink=true)
Definition simplevec.h:547
int Count(void) const
Definition simplevec.h:263
bool Add(T const &object, int new_size_hint=0)
Definition simplevec.h:371
float Radius
Definition sphere.h:91
Vector3 Center
Definition sphere.h:90
float Y
Definition vector2.h:79
float X
Definition vector2.h:74
float X
Definition vector3.h:90
float Z
Definition vector3.h:92
float Y
Definition vector3.h:91
WWINLINE void Set(float x, float y, float z, float w)
Definition vector4.h:80
float W
Definition vector4.h:69
static void Clamp(Vector4 *dst, const Vector4 *src, const float min, const float max, const int count)
Definition vp.cpp:459
static void Copy(unsigned *dst, const unsigned *src, const int count)
Definition vp.cpp:333
float Height(void) const
Definition camera.h:78
float Width(void) const
Definition camera.h:77
Definition ww3d.h:78
static unsigned int Get_Sync_Time(void)
Definition ww3d.h:172
static unsigned int Get_Frame_Count(void)
Definition ww3d.h:174
static void Add_To_Static_Sort_List(RenderObjClass *robj, unsigned int sort_level)
Definition ww3d.cpp:1984
static bool Are_Static_Sort_Lists_Enabled(void)
Definition ww3d.h:282
static bool Is_Sorting_Enabled(void)
Definition ww3d.h:220
const float oo_intmax
Definition part_buf.cpp:78
else return(RetVal)
#define REF_PTR_RELEASE(x)
Definition refcount.h:80
#define NEW_REF(C, P)
Definition refcount.h:62
Vector3 Velocity
Definition part_buf.h:63
unsigned int TimeStamp
Definition part_buf.h:64
unsigned char GroupID
Definition part_buf.h:65
Vector3 Position
Definition part_buf.h:62
unsigned int NumKeyFrames
Definition part_emt.h:65
#define WWPROFILE(name)
Definition wwprofile.h:270