1 /*---------------------------------------------------------------------------*
2 Project: NintendoWare
3 File: gfx_ParticleSet.cpp
4
5 Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc. All rights reserved.
6
7 These coded instructions, statements, and computer programs contain
8 proprietary information of Nintendo of America Inc. and/or Nintendo
9 Company Ltd., and are protected by Federal copyright law. They may
10 not be disclosed to third parties or copied or duplicated in any form,
11 in whole or in part, without the prior written consent of Nintendo.
12
13 $Revision: 28126 $
14 *---------------------------------------------------------------------------*/
15
16 #include "precompiled.h"
17
18 #include <nw/gfx/gfx_ParticleContext.h>
19 #include <nw/gfx/gfx_ParticleSet.h>
20 #include <nw/gfx/gfx_ParticleModel.h>
21 #include <nw/gfx/gfx_ParticleEmitter.h>
22 #include <nw/gfx/gfx_ParticleTime.h>
23 #include <nw/gfx/gfx_ISceneVisitor.h>
24
25 #include <nw/dev/dev_ParticleProfile.h>
26
27 #include <nw/ut/ut_ResUtil.h>
28 #include <nw/ut/ut_ResDictionary.h>
29
30 namespace nw
31 {
32 namespace gfx
33 {
34
35 namespace internal
36 {
37 #if 0
38 static const f32 acosTable[] =
39 {
40 3.141593f,3.067040f,3.036135f,3.012403f,2.992383f,2.974733f,2.958764f,2.944069f,2.930382f,2.917517f,2.905342f,2.893752f,
41 2.882671f,2.872035f,2.861794f,2.851906f,2.842335f,2.833052f,2.824032f,2.815253f,2.806697f,2.798345f,2.790184f,2.782202f,
42 2.774385f,2.766724f,2.759209f,2.751832f,2.744585f,2.737462f,2.730455f,2.723559f,2.716768f,2.710078f,2.703484f,2.696981f,
43 2.690566f,2.684235f,2.677984f,2.671810f,2.665710f,2.659682f,2.653723f,2.647830f,2.642000f,2.636232f,2.630524f,2.624873f,
44 2.619278f,2.613737f,2.608248f,2.602809f,2.597420f,2.592077f,2.586782f,2.581531f,2.576324f,2.571159f,2.566035f,2.560952f,
45 2.555907f,2.550901f,2.545932f,2.540999f,2.536101f,2.531238f,2.526408f,2.521611f,2.516846f,2.512112f,2.507409f,2.502736f,
46 2.498092f,2.493476f,2.488889f,2.484329f,2.479795f,2.475288f,2.470807f,2.466351f,2.461919f,2.457511f,2.453128f,2.448767f,
47 2.444430f,2.440114f,2.435821f,2.431549f,2.427298f,2.423068f,2.418859f,2.414669f,2.410499f,2.406348f,2.402216f,2.398103f,
48 2.394008f,2.389932f,2.385872f,2.381831f,2.377806f,2.373798f,2.369807f,2.365833f,2.361874f,2.357931f,2.354003f,2.350091f,
49 2.346194f,2.342312f,2.338444f,2.334590f,2.330751f,2.326926f,2.323115f,2.319317f,2.315532f,2.311761f,2.308003f,2.304257f,
50 2.300524f,2.296803f,2.293095f,2.289399f,2.285714f,2.282042f,2.278381f,2.274731f,2.271093f,2.267466f,2.263849f,2.260244f,
51 2.256649f,2.253065f,2.249491f,2.245928f,2.242375f,2.238831f,2.235298f,2.231774f,2.228260f,2.224755f,2.221260f,2.217774f,
52 2.214298f,2.210830f,2.207371f,2.203921f,2.200480f,2.197047f,2.193623f,2.190207f,2.186800f,2.183401f,2.180009f,2.176626f,
53 2.173251f,2.169884f,2.166524f,2.163172f,2.159827f,2.156490f,2.153161f,2.149838f,2.146523f,2.143215f,2.139914f,2.136620f,
54 2.133333f,2.130052f,2.126779f,2.123511f,2.120251f,2.116997f,2.113750f,2.110508f,2.107273f,2.104045f,2.100822f,2.097606f,
55 2.094395f,2.091191f,2.087992f,2.084799f,2.081612f,2.078431f,2.075255f,2.072084f,2.068920f,2.065760f,2.062606f,2.059458f,
56 2.056314f,2.053176f,2.050043f,2.046916f,2.043792f,2.040675f,2.037562f,2.034454f,2.031350f,2.028252f,2.025158f,2.022069f,
57 2.018985f,2.015904f,2.012829f,2.009758f,2.006692f,2.003630f,2.000572f,1.997518f,1.994469f,1.991424f,1.988383f,1.985346f,
58 1.982313f,1.979284f,1.976260f,1.973239f,1.970222f,1.967208f,1.964199f,1.961193f,1.958191f,1.955193f,1.952199f,1.949207f,
59 1.946220f,1.943236f,1.940256f,1.937278f,1.934305f,1.931334f,1.928367f,1.925404f,1.922443f,1.919486f,1.916532f,1.913581f,
60 1.910633f,1.907688f,1.904747f,1.901808f,1.898872f,1.895939f,1.893010f,1.890083f,1.887158f,1.884237f,1.881318f,1.878402f,
61 1.875489f,1.872578f,1.869671f,1.866765f,1.863862f,1.860962f,1.858064f,1.855169f,1.852276f,1.849386f,1.846498f,1.843612f,
62 1.840729f,1.837848f,1.834969f,1.832093f,1.829219f,1.826347f,1.823477f,1.820609f,1.817743f,1.814879f,1.812018f,1.809158f,
63 1.806301f,1.803445f,1.800591f,1.797739f,1.794889f,1.792041f,1.789195f,1.786351f,1.783508f,1.780667f,1.777828f,1.774990f,
64 1.772154f,1.769320f,1.766487f,1.763656f,1.760827f,1.757999f,1.755172f,1.752348f,1.749524f,1.746702f,1.743881f,1.741062f,
65 1.738244f,1.735428f,1.732613f,1.729799f,1.726986f,1.724175f,1.721365f,1.718556f,1.715748f,1.712941f,1.710136f,1.707331f,
66 1.704528f,1.701726f,1.698924f,1.696124f,1.693325f,1.690527f,1.687729f,1.684933f,1.682137f,1.679343f,1.676549f,1.673756f,
67 1.670964f,1.668172f,1.665382f,1.662592f,1.659803f,1.657014f,1.654226f,1.651439f,1.648653f,1.645867f,1.643081f,1.640297f,
68 1.637512f,1.634729f,1.631945f,1.629163f,1.626380f,1.623599f,1.620817f,1.618036f,1.615255f,1.612475f,1.609695f,1.606915f,
69 1.604136f,1.601357f,1.598578f,1.595799f,1.593020f,1.590242f,1.587464f,1.584686f,1.581908f,1.579130f,1.576352f,1.573574f,
70 1.570796f,1.568019f,1.565241f,1.562463f,1.559685f,1.556907f,1.554129f,1.551351f,1.548572f,1.545794f,1.543015f,1.540236f,
71 1.537457f,1.534677f,1.531898f,1.529118f,1.526337f,1.523557f,1.520775f,1.517994f,1.515212f,1.512430f,1.509647f,1.506864f,
72 1.504080f,1.501296f,1.498511f,1.495726f,1.492940f,1.490153f,1.487366f,1.484578f,1.481790f,1.479001f,1.476211f,1.473420f,
73 1.470629f,1.467837f,1.465044f,1.462250f,1.459455f,1.456660f,1.453863f,1.451066f,1.448268f,1.445469f,1.442668f,1.439867f,
74 1.437065f,1.434261f,1.431457f,1.428651f,1.425845f,1.423037f,1.420228f,1.417418f,1.414606f,1.411794f,1.408980f,1.406165f,
75 1.403348f,1.400530f,1.397711f,1.394891f,1.392069f,1.389245f,1.386420f,1.383594f,1.380766f,1.377936f,1.375105f,1.372273f,
76 1.369438f,1.366603f,1.363765f,1.360926f,1.358085f,1.355242f,1.352398f,1.349551f,1.346703f,1.343853f,1.341002f,1.338148f,
77 1.335292f,1.332434f,1.329575f,1.326713f,1.323850f,1.320984f,1.318116f,1.315246f,1.312374f,1.309500f,1.306623f,1.303745f,
78 1.300864f,1.297980f,1.295095f,1.292207f,1.289316f,1.286423f,1.283528f,1.280631f,1.277730f,1.274827f,1.271922f,1.269014f,
79 1.266104f,1.263190f,1.260275f,1.257356f,1.254434f,1.251510f,1.248583f,1.245653f,1.242720f,1.239785f,1.236846f,1.233904f,
80 1.230959f,1.228012f,1.225061f,1.222107f,1.219149f,1.216189f,1.213225f,1.210258f,1.207288f,1.204314f,1.201337f,1.198357f,
81 1.195373f,1.192385f,1.189394f,1.186400f,1.183401f,1.180399f,1.177394f,1.174384f,1.171371f,1.168354f,1.165333f,1.162308f,
82 1.159279f,1.156247f,1.153210f,1.150169f,1.147124f,1.144074f,1.141021f,1.137963f,1.134901f,1.131835f,1.128763f,1.125688f,
83 1.122608f,1.119524f,1.116435f,1.113341f,1.110242f,1.107139f,1.104031f,1.100918f,1.097800f,1.094677f,1.091549f,1.088416f,
84 1.085278f,1.082135f,1.078986f,1.075832f,1.072673f,1.069508f,1.066338f,1.063162f,1.059981f,1.056794f,1.053601f,1.050402f,
85 1.047198f,1.043987f,1.040771f,1.037548f,1.034319f,1.031084f,1.027843f,1.024596f,1.021342f,1.018081f,1.014814f,1.011541f,
86 1.008260f,1.004973f,1.001679f,0.998378f,0.995070f,0.991754f,0.988432f,0.985102f,0.981765f,0.978421f,0.975069f,0.971709f,
87 0.968342f,0.964966f,0.961583f,0.958192f,0.954793f,0.951385f,0.947970f,0.944546f,0.941113f,0.937672f,0.934222f,0.930763f,
88 0.927295f,0.923818f,0.920332f,0.916837f,0.913333f,0.909819f,0.906295f,0.902762f,0.899218f,0.895665f,0.892101f,0.888527f,
89 0.884943f,0.881349f,0.877743f,0.874127f,0.870500f,0.866862f,0.863212f,0.859551f,0.855878f,0.852194f,0.848498f,0.844789f,
90 0.841069f,0.837336f,0.833590f,0.829832f,0.826060f,0.822276f,0.818478f,0.814666f,0.810841f,0.807002f,0.803149f,0.799281f,
91 0.795399f,0.791502f,0.787590f,0.783662f,0.779719f,0.775760f,0.771785f,0.767794f,0.763786f,0.759762f,0.755720f,0.751661f,
92 0.747584f,0.743490f,0.739376f,0.735245f,0.731094f,0.726924f,0.722734f,0.718525f,0.714295f,0.710044f,0.705772f,0.701478f,
93 0.697163f,0.692825f,0.688465f,0.684081f,0.679674f,0.675242f,0.670786f,0.666305f,0.661797f,0.657264f,0.652704f,0.648116f,
94 0.643501f,0.638857f,0.634184f,0.629481f,0.624747f,0.619982f,0.615185f,0.610355f,0.605492f,0.600594f,0.595661f,0.590692f,
95 0.585685f,0.580641f,0.575558f,0.570434f,0.565269f,0.560062f,0.554811f,0.549515f,0.544173f,0.538784f,0.533345f,0.527856f,
96 0.522315f,0.516720f,0.511069f,0.505360f,0.499593f,0.493763f,0.487870f,0.481910f,0.475882f,0.469783f,0.463609f,0.457358f,
97 0.451027f,0.444612f,0.438109f,0.431514f,0.424824f,0.418034f,0.411138f,0.404131f,0.397007f,0.389761f,0.382384f,0.374869f,
98 0.367208f,0.359391f,0.351408f,0.343248f,0.334896f,0.326339f,0.317560f,0.308540f,0.299258f,0.289687f,0.279798f,0.269557f,
99 0.258921f,0.247840f,0.236251f,0.224075f,0.211211f,0.197523f,0.182829f,0.166860f,0.149209f,0.129189f,0.105458f,0.074553f,
100 0.000000f,
101 };
102
103 // ArcCosのテーブル引き関数です。
104 inline f32
105 AcosTableRad(f32 x)
106 {
107 // 0~720へ変換
108 u32 index = x * 360 + 360;
109 if (index < sizeof(acosTable))
110 {
111 return acosTable[index];
112 }
113 else
114 {
115 return 0.0f;
116 }
117 }
118 #endif
119
120 inline bool
VEC3Normalize(nn::math::VEC3 * pIn)121 VEC3Normalize(nn::math::VEC3* pIn)
122 {
123 NN_NULL_ASSERT(pIn);
124
125 f32 mag = (pIn->x * pIn->x) + (pIn->y * pIn->y) + (pIn->z * pIn->z);
126
127 if (mag == 0)
128 {
129 return false;
130 }
131
132 mag = 1.0f / ::std::sqrtf(mag);
133
134 pIn->x = pIn->x * mag;
135 pIn->y = pIn->y * mag;
136 pIn->z = pIn->z * mag;
137
138 return true;
139 }
140
141
142 #include <nn/hw/ARM/code32.h>
143 #include <nn/hw/ARM/VFPv2_reg.h>
144
145 // ベクタ長4 バージョン
146 asm void
VEC3ArrayAddScalar(nw::math::VEC3 *,nw::math::VEC3 *,u32)147 VEC3ArrayAddScalar(nw::math::VEC3* /*array*/, nw::math::VEC3* /*scalar*/, u32 /*countDiv12*/)
148 {
149 // レジスタの保存
150 VPUSH {d8-d11}
151
152 ADD r2, #3
153 ASR r2, #2
154
155 // 端数処理を省けるよう、VEC3を4個ずつ一度に処理
156 VLDMIA r1, {s20-s22}
157 MOV r3, r0
158
159 VEC3ArrayAddScalarLoop
160 VLDMIA r0!, {s8-s11}
161 VLDMIA r0!, {s12-s15}
162
163 // [s8 .. s10] += [s20 .. s22]
164 // [s11 .. s13] += [s20 .. s22]
165 // [s14 .. s16] += [s20 .. s22]
166 // [s17 .. s19] += [s20 .. s22]
167 VADD.F32 s8, s8, s20
168 VADD.F32 s9, s9, s21
169 VADD.F32 s10, s10, s22
170 VADD.F32 s11, s11, s20
171
172 VLDMIA r0!, {s16-s19}
173
174 VADD.F32 s12, s12, s21
175 VADD.F32 s13, s13, s22
176 VADD.F32 s14, s14, s20
177 VADD.F32 s15, s15, s21
178
179 VSTMIA r3!, {s8-s11}
180
181 VADD.F32 s16, s16, s22
182 VADD.F32 s17, s17, s20
183 VADD.F32 s18, s18, s21
184 VADD.F32 s19, s19, s22
185
186 VSTMIA r3!, {s12-s19}
187
188 SUBS r2, r2, #1
189 BNE VEC3ArrayAddScalarLoop
190
191 // レジスタの復帰
192 VPOP {d8-d11}
193
194 // 戻る
195 BX lr
196 }
197
198 asm void
VEC3ArrayAddVEC3Array(nw::math::VEC3 *,nw::math::VEC3 *,f32,u32)199 VEC3ArrayAddVEC3Array(
200 nw::math::VEC3* /*arrayTarget*/, // r0
201 nw::math::VEC3* /*arrayAdd*/, // r1
202 f32 /*scalar*/, // s0
203 u32 /*countDiv12*/ // r2
204 )
205 {
206 // レジスタの保存
207 VPUSH {d8-d15}
208
209 ADD r2, #3
210 ASR r2, #2
211
212 MOV r3, r0
213
214 // 端数処理を省けるよう、VEC3を4個ずつ一度に処理
215 VEC3ArrayAddVEC3ArrayLoop
216 VLDMIA r0!, {s8-s11}
217 VLDMIA r1!, {s20-s23}
218 VLDMIA r0!, {s12-s15}
219
220 // [s8 .. s11] += [s20 .. s23] * s0
221 VMLA.F32 s8, s20, s0
222 VMLA.F32 s9, s21, s0
223 VMLA.F32 s10, s22, s0
224 VMLA.F32 s11, s23, s0
225
226 VLDMIA r1!, {s24-s27}
227 VSTMIA r3!, {s8-s11}
228
229 // [s12 .. s15] += [s24 .. s27] * s0
230 VMLA.F32 s12, s24, s0
231 VMLA.F32 s13, s25, s0
232
233 VLDMIA r0!, {s16-s19}
234
235 VMLA.F32 s14, s26, s0
236 VMLA.F32 s15, s27, s0
237
238 VLDMIA r1!, {s28-s31}
239 VSTMIA r3!, {s12-s15}
240
241 // [s16 .. s19] += [s28 .. s31] * s0
242 VMLA.F32 s16, s28, s0
243 VMLA.F32 s17, s29, s0
244 VMLA.F32 s18, s30, s0
245 VMLA.F32 s19, s31, s0
246
247 VSTMIA r3!, {s16-s19}
248
249 SUBS r2, r2, #1
250 BNE VEC3ArrayAddVEC3ArrayLoop
251
252 // レジスタの復帰
253 VPOP {d8-d15}
254
255 // 戻る
256 BX lr
257 }
258
259 asm void
CalcRemainFrame(f32 *,f32 *,f32,int)260 CalcRemainFrame(
261 f32* /*output*/, // r0
262 f32* /*limitArray*/, // r1
263 f32 /*time*/, // s0
264 int /*count*/ // r2
265 )
266 {
267 ADD r2, #7
268 ASR r2, #3
269
270 CalcRemainFrameLoop
271 VLDMIA r1!, {s8-s15}
272
273 // [s8 .. s15] = [s8 .. s15] + s0
274 VADD.F32 s8, s8, s0
275 VADD.F32 s9, s9, s0
276 VADD.F32 s10, s10, s0
277 VADD.F32 s11, s11, s0
278 VADD.F32 s12, s12, s0
279 VADD.F32 s13, s13, s0
280 VADD.F32 s14, s14, s0
281 VADD.F32 s15, s15, s0
282
283 VSTMIA r0!, {s8-s15}
284
285 SUBS r2, r2, #1
286 BNE CalcRemainFrameLoop
287
288 // 戻る
289 BX lr
290 }
291
292 #include <nn/hw/ARM/codereset.h>
293
294 }
295
296 NW_UT_RUNTIME_TYPEINFO_DEFINITION(ParticleSet, SceneNode);
297
298 void
GetMemorySizeInternal(os::MemorySizeCalculator * pSize,ResParticleSet resNode,const ParticleSet::Description & description)299 ParticleSet::GetMemorySizeInternal(
300 os::MemorySizeCalculator* pSize,
301 ResParticleSet resNode,
302 const ParticleSet::Description& description)
303 {
304 int initializerCapacity = description.maxInitializers;
305 if (initializerCapacity < resNode.GetParticleInitializersCount())
306 {
307 initializerCapacity = resNode.GetParticleInitializersCount();
308 }
309
310 int updaterCapacity = description.maxUpdaters;
311 if (updaterCapacity < resNode.GetParticleUpdatersCount())
312 {
313 updaterCapacity = resNode.GetParticleUpdatersCount();
314 }
315
316 os::MemorySizeCalculator& size = *pSize;
317
318 size.Add(sizeof(ParticleSet), 4);
319 size.Add(sizeof(Initializer) * initializerCapacity, 4);
320 size.Add(sizeof(Updater) * updaterCapacity, 4);
321
322 {
323 ResParticleInitializerArray resInitializers =
324 resNode.GetParticleInitializers();
325 ResParticleInitializerArrayIterator initializerEnd =
326 resInitializers.end();
327 for (ResParticleInitializerArrayIterator i = resInitializers.begin(); i != initializerEnd;)
328 {
329 const ResParticleInitializer resInitializer = *i++;
330
331 if (resInitializer.GetIsResourceCopyEnabled())
332 {
333 ParticleUtil::GetMemorySizeForDuplicateResParticleInitializerInternal(
334 pSize,&resInitializer);
335 }
336 }
337 }
338
339 {
340 ResParticleUpdaterArray resUpdaters = resNode.GetParticleUpdaters();
341 ResParticleUpdaterArrayIterator updaterEnd =
342 resUpdaters.end();
343 for (ResParticleUpdaterArrayIterator i = resUpdaters.begin(); i != updaterEnd;)
344 {
345 const ResParticleUpdater resUpdater = *i++;
346
347 if (resUpdater.GetIsResourceCopyEnabled())
348 {
349 ParticleUtil::GetMemorySizeForDuplicateResParticleUpdaterInternal(
350 pSize, &resUpdater);
351 }
352 }
353 }
354
355 SceneNode::GetMemorySizeForInitialize(pSize, resNode, description);
356
357 nw::gfx::ResParticleCollection resParticleCollection =
358 resNode.GetParticleCollection();
359
360 NW_ASSERT(resParticleCollection.IsValid());
361
362 ParticleCollection::GetMemorySizeInternal(pSize, resParticleCollection);
363 }
364
365 //----------------------------------------
366 void
GetDeviceMemorySizeInternal(os::MemorySizeCalculator * pSize,ResParticleSet resNode)367 ParticleSet::GetDeviceMemorySizeInternal(
368 os::MemorySizeCalculator* pSize,
369 ResParticleSet resNode)
370 {
371 nw::gfx::ResParticleCollection resParticleCollection =
372 resNode.GetParticleCollection();
373
374 ParticleCollection::GetDeviceMemorySizeInternal(pSize, resParticleCollection);
375 }
376
377 //----------------------------------------
378 ParticleSet*
Create(SceneNode * parent,ResSceneObject resource,const ParticleSet::Description & description,os::IAllocator * mainAllocator,os::IAllocator * deviceAllocator,ParticleShape * shape)379 ParticleSet::Create(
380 SceneNode* parent,
381 ResSceneObject resource,
382 const ParticleSet::Description& description,
383 os::IAllocator* mainAllocator,
384 os::IAllocator* deviceAllocator,
385 ParticleShape* shape
386 )
387 {
388 NW_NULL_ASSERT(mainAllocator);
389 NW_NULL_ASSERT(deviceAllocator);
390
391 ResParticleSet resNode = ResDynamicCast<ResParticleSet>(resource);
392 NW_ASSERT(resNode.IsValid());
393
394 int initializerCapacity = description.maxInitializers;
395 if (initializerCapacity < resNode.GetParticleInitializersCount())
396 {
397 initializerCapacity = resNode.GetParticleInitializersCount();
398 }
399
400 int updaterCapacity = description.maxUpdaters;
401 if (updaterCapacity < resNode.GetParticleUpdatersCount())
402 {
403 updaterCapacity = resNode.GetParticleUpdatersCount();
404 }
405
406 int memorySize = sizeof(ParticleSet);
407 memorySize = ut::RoundUp(memorySize, 4);
408 memorySize += sizeof(Initializer) * initializerCapacity;
409 memorySize = ut::RoundUp(memorySize, 4);
410 memorySize += sizeof(Updater) * updaterCapacity;
411
412 u8* memory = reinterpret_cast<u8*>(mainAllocator->Alloc(memorySize));
413 if (memory == NULL)
414 {
415 return NULL;
416 }
417
418 ParticleSet* node = new(memory) ParticleSet(
419 mainAllocator,
420 resNode,
421 description);
422 memory += sizeof(ParticleSet);
423
424 {
425 Result result = node->Initialize(mainAllocator);
426 if (!result.IsSuccess())
427 {
428 SafeDestroy(node);
429 return NULL;
430 }
431 }
432
433 if (parent)
434 {
435 bool result = parent->AttachChild(node);
436 NW_ASSERT(result);
437 }
438
439 bool isSuccess = true;
440
441 {
442 memory = reinterpret_cast<u8*>(ut::RoundUp(memory, 4));
443 node->m_Initializers = ut::MoveArray<Initializer>(memory, initializerCapacity);
444 memory += sizeof(Initializer) * initializerCapacity;
445
446 ResParticleInitializerArray resInitializers =
447 resNode.GetParticleInitializers();
448 ResParticleInitializerArrayIterator initializerEnd =
449 resInitializers.end();
450 for (ResParticleInitializerArrayIterator i = resInitializers.begin(); i != initializerEnd;)
451 {
452 const ResParticleInitializer resInitializer = *i++;
453
454 Initializer initializer;
455
456 if (resInitializer.GetIsResourceCopyEnabled())
457 {
458 initializer.m_IsCopied = true;
459 initializer.resource = ParticleUtil::DuplicateResParticleInitializer(
460 &resInitializer,
461 mainAllocator);
462 if (initializer.resource == NULL)
463 {
464 isSuccess = false;
465 }
466 }
467 else
468 {
469 initializer.m_IsCopied = false;
470 initializer.resource = resInitializer.ptr();
471 }
472
473 initializer.work = 0;
474
475 nw::ut::ResTypeInfo resType = resInitializer.GetTypeInfo();
476 initializer.m_Type = resType;
477
478 node->m_Initializers.push_back(initializer);
479 }
480 }
481
482 {
483 memory = reinterpret_cast<u8*>(ut::RoundUp(memory, 4));
484 node->m_Updaters = ut::MoveArray<Updater>(memory, updaterCapacity);
485 memory += sizeof(Updater) * updaterCapacity;
486
487 ResParticleUpdaterArray resUpdaters = resNode.GetParticleUpdaters();
488 ResParticleUpdaterArrayIterator updaterEnd =
489 resUpdaters.end();
490 for (ResParticleUpdaterArrayIterator i = resUpdaters.begin(); i != updaterEnd;)
491 {
492 const ResParticleUpdater resUpdater = *i++;
493
494 Updater updater;
495
496 if (resUpdater.GetIsResourceCopyEnabled())
497 {
498 updater.m_IsCopied = true;
499 updater.resource = ParticleUtil::DuplicateResParticleUpdater(
500 &resUpdater,
501 mainAllocator);
502 if (updater.resource == NULL)
503 {
504 isSuccess = false;
505 }
506 }
507 else
508 {
509 updater.m_IsCopied = false;
510 updater.resource = resUpdater.ptr();
511 }
512
513 updater.work = 0;
514 updater.m_Flags = 0;
515
516 nw::ut::ResTypeInfo resType = resUpdater.GetTypeInfo();
517 updater.m_Type = resType;
518
519 // アニメーション系のアップデータであれば、
520 // カーブデータがあるかこの時点で判断する。
521 if ((resType == ResParticleVector3AdditiveUpdater::TYPE_INFO) ||
522 (resType == ResParticleVector3ImmediateUpdater::TYPE_INFO) ||
523 (resType == ResParticleVector3RandomAdditiveUpdater::TYPE_INFO))
524 {
525 ResParticleVector3Updater vec3Updater(resUpdater.ptr());
526 NW_ASSERT(vec3Updater.IsValid());
527 ResParticleAnimation animation = vec3Updater.GetParticleAnimation();
528 if (animation.IsValid())
529 {
530 if (animation.GetAnimationData())
531 {
532 updater.EnableFlags(Updater::FLAG_IS_HAS_CURVE_ANIM);
533 }
534 }
535 }
536
537 node->m_Updaters.push_back(updater);
538 }
539 }
540
541 if (isSuccess == false)
542 {
543 SafeDestroy(node);
544 return NULL;
545 }
546
547 nw::gfx::ResParticleCollection resParticleCollection =
548 resNode.GetParticleCollection();
549
550 NW_ASSERT(resParticleCollection.IsValid());
551
552 ParticleCollection* collectionNode = ParticleCollection::Create(
553 node,
554 resParticleCollection,
555 mainAllocator,
556 deviceAllocator,
557 shape);
558
559 if (collectionNode == NULL)
560 {
561 SafeDestroy(node);
562 return NULL;
563 }
564
565 shape->CreateCommandCache(node);
566
567 // 高速化のためのストリームのポインタのキャッシュ
568 {
569 ut::MoveArray<Initializer>::iterator endIter = node->m_Initializers.end();
570 for (ut::MoveArray<Initializer>::iterator iter = node->m_Initializers.begin(); iter != endIter;)
571 {
572 Initializer& initializer = *iter++;
573
574 const ResParticleInitializer resInitializer(initializer.resource);
575
576 ParticleUsage target = resInitializer.GetTargetStream();
577
578 if (target >= 0)
579 {
580 if (collectionNode->GetBufferSide())
581 {
582 initializer.m_TargetStreams[1] = collectionNode->GetStreamPtr(target, PARTICLE_BUFFER_FRONT);
583 initializer.m_TargetStreams[0] = collectionNode->GetStreamPtr(target, PARTICLE_BUFFER_BACK);
584 }
585 else
586 {
587 initializer.m_TargetStreams[0] = collectionNode->GetStreamPtr(target, PARTICLE_BUFFER_FRONT);
588 initializer.m_TargetStreams[1] = collectionNode->GetStreamPtr(target, PARTICLE_BUFFER_BACK);
589 }
590 }
591 else
592 {
593 initializer.m_TargetStreams[0] = NULL;
594 initializer.m_TargetStreams[1] = NULL;
595 }
596 }
597 }
598
599 {
600 ut::MoveArray<Updater>::iterator endIter = node->m_Updaters.end();
601 for (ut::MoveArray<Updater>::iterator iter = node->m_Updaters.begin(); iter != endIter;)
602 {
603 Updater& updater = *iter++;
604
605 const ResParticleUpdater resUpdater(updater.resource);
606
607 ParticleUsage target = resUpdater.GetTargetStream();
608
609 if (target >= 0)
610 {
611 if (collectionNode->GetBufferSide())
612 {
613 updater.m_TargetStreams[1] = collectionNode->GetStreamPtr(target, PARTICLE_BUFFER_FRONT);
614 updater.m_TargetStreams[0] = collectionNode->GetStreamPtr(target, PARTICLE_BUFFER_BACK);
615 }
616 else
617 {
618 updater.m_TargetStreams[0] = collectionNode->GetStreamPtr(target, PARTICLE_BUFFER_FRONT);
619 updater.m_TargetStreams[1] = collectionNode->GetStreamPtr(target, PARTICLE_BUFFER_BACK);
620 }
621 }
622 else
623 {
624 updater.m_TargetStreams[0] = NULL;
625 updater.m_TargetStreams[1] = NULL;
626 }
627 }
628 }
629
630 return node;
631 }
632
633 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
634 // CALC_OPT:inline 指定へ。
635 /*static*/ inline void
SetDefaultValues(ParticleCollection * collection,const nw::math::VEC3 * positions,int particleCount,f32 time,bool isAscendingOrder)636 SetDefaultValues(
637 ParticleCollection* collection,
638 const nw::math::VEC3* positions,
639 int particleCount,
640 f32 time,
641 bool isAscendingOrder
642 )
643 {
644 ParticleTime lifeParam = 1;
645 if (!collection->IsStream(PARTICLEUSAGE_LIFE))
646 {
647 lifeParam = *(ParticleTime*)collection->GetParameterPtr(PARTICLEUSAGE_LIFE);
648 }
649
650 u16* activeIndex = (u16*)collection->GetStreamPtr(PARTICLEUSAGE_ACTIVEINDEX, PARTICLE_BUFFER_FRONT);
651 NW_NULL_ASSERT(activeIndex);
652
653 const int startIndex = (isAscendingOrder)? collection->GetCount() : collection->GetCapacity() - collection->GetCount() - 1;
654 const int incrIndex = (isAscendingOrder)? 1 : -1;
655
656 u16* pFreeIndex = (u16*)collection->GetStreamPtr(PARTICLEUSAGE_FREEINDEX, PARTICLE_BUFFER_FRONT);
657 NW_NULL_ASSERT(pFreeIndex);
658 pFreeIndex += collection->GetCapacity() - collection->GetCount() - 1;
659
660 ParticleTime* birth = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_BIRTH, PARTICLE_BUFFER_FRONT);
661 NW_NULL_ASSERT(birth);
662
663 nw::math::VEC3* translate = (nw::math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_TRANSLATE, PARTICLE_BUFFER_FRONT);
664 NW_NULL_ASSERT(translate);
665
666 nw::math::VEC3* velocity = (nw::math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_VELOCITY, PARTICLE_BUFFER_FRONT);
667 NW_NULL_ASSERT(velocity);
668
669 ParticleTime* limit = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_NEG_TIMELIMIT, PARTICLE_BUFFER_FRONT);
670 NW_NULL_ASSERT(limit);
671
672 int minIndex = collection->GetMinActiveIndex();
673 int maxIndex = collection->GetMaxActiveIndex();
674 {
675 f32 limitDefault = -(time + lifeParam);
676
677 u16* pActiveIndex = activeIndex + startIndex;
678 for (int i = particleCount; i != 0; --i)
679 {
680 int nextIndex = *pFreeIndex;
681 --pFreeIndex;
682
683 *pActiveIndex = nextIndex;
684 pActiveIndex += incrIndex;
685
686 if (minIndex > nextIndex)
687 {
688 minIndex = nextIndex;
689 }
690
691 if (maxIndex < nextIndex)
692 {
693 maxIndex = nextIndex;
694 }
695
696 birth[nextIndex] = time;
697 translate[nextIndex] = *positions++;
698 velocity[nextIndex].x = 0.0f;
699 velocity[nextIndex].y = 0.0f;
700 velocity[nextIndex].z = 0.0f;
701 limit[nextIndex] = limitDefault;
702 }
703 }
704
705 collection->SetMinActiveIndex(minIndex);
706 collection->SetMaxActiveIndex(maxIndex);
707 }
708
709 //----------------------------------------
710 void
AddParticles(const nw::math::MTX34 & emitterMatrix,const nw::math::VEC3 * const positions,ParticleSet * parentParticleSet,const u16 * const parentIndices,int positionsCount)711 ParticleSet::AddParticles(
712 const nw::math::MTX34& emitterMatrix,
713 const nw::math::VEC3* const positions,
714 ParticleSet* parentParticleSet,
715 const u16* const parentIndices,
716 int positionsCount
717 )
718 {
719 NW_NULL_ASSERT(positions);
720
721 ParticleCollection* collection = this->GetParticleCollection();
722
723 const int collectionCount = collection->GetCount();
724 const int collectionCapacity = collection->GetCapacity();
725
726 int freeSize = collectionCapacity - collectionCount;
727 if (freeSize <= 0)
728 {
729 return;
730 }
731
732 int particleCount = positionsCount;
733 if (particleCount > freeSize)
734 {
735 particleCount = freeSize;
736 }
737
738 if (particleCount <= 0)
739 {
740 return;
741 }
742
743 const bool isAscendingOrder = this->IsAscendingOrder();
744
745 const int startIndex = (isAscendingOrder)? collectionCount : collectionCapacity - collectionCount - 1;
746 const int incrIndex = (isAscendingOrder)? 1 : -1;
747
748 ParticleModel* model = static_cast<ParticleModel*>(this->GetParent());
749 NW_NULL_ASSERT(model);
750 f32 time = model->ParticleAnimFrameController().GetFrame();
751
752 SetDefaultValues(collection, positions, particleCount, time, isAscendingOrder);
753
754 this->InitializeParticles(startIndex, particleCount, incrIndex, time);
755
756 collection->SetCount(collectionCount + particleCount);
757
758 bool useTransform = false;
759 if (this->WorldMatrix() != emitterMatrix)
760 {
761 useTransform = true;
762 }
763
764 if (parentIndices == NULL)
765 {
766 if (useTransform)
767 {
768 nw::math::MTX34 transformMatrix;
769 nw::math::MTX34Mult(&transformMatrix, this->InverseWorldMatrix(), emitterMatrix);
770
771 u16* activeIndex = (u16*)collection->GetStreamPtr(PARTICLEUSAGE_ACTIVEINDEX, PARTICLE_BUFFER_FRONT);
772
773 math::VEC3* translate =
774 (math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_TRANSLATE, PARTICLE_BUFFER_FRONT);
775
776 math::VEC3* velocity =
777 (math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_VELOCITY, PARTICLE_BUFFER_FRONT);
778
779 u16* pActiveIndex = activeIndex + startIndex;
780 for (int i = 0; i < particleCount; ++i)
781 {
782 int dstIndex = *pActiveIndex;
783 pActiveIndex += incrIndex;
784
785 nw::math::VEC3Transform(&translate[dstIndex], transformMatrix, translate[dstIndex]);
786 nw::math::VEC3TransformNormal(&velocity[dstIndex], transformMatrix, velocity[dstIndex]);
787 }
788 }
789 }
790 else
791 {
792 math::VEC3* parentTranslate =
793 (math::VEC3*)parentParticleSet->GetParticleCollection()->GetStreamPtr(
794 PARTICLEUSAGE_TRANSLATE, PARTICLE_BUFFER_FRONT);
795
796 u16* activeIndex = (u16*)collection->GetStreamPtr(PARTICLEUSAGE_ACTIVEINDEX, PARTICLE_BUFFER_FRONT);
797
798 math::VEC3* translate =
799 (math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_TRANSLATE, PARTICLE_BUFFER_FRONT);
800
801 math::VEC3* velocity =
802 (math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_VELOCITY, PARTICLE_BUFFER_FRONT);
803
804 u16* pActiveIndex = activeIndex + startIndex;
805
806 // 親の速度
807 bool isInheritParentVelocity = this->GetResParticleSet().GetIsInheritParentVelocity();
808
809 math::VEC3* parentVelocity = NULL;
810 if (isInheritParentVelocity)
811 {
812 parentVelocity = (math::VEC3*)parentParticleSet->GetParticleCollection()->GetStreamPtr(
813 PARTICLEUSAGE_VELOCITY, PARTICLE_BUFFER_FRONT);
814 }
815
816 if (useTransform)
817 {
818 for (int i = 0; i < particleCount; ++i)
819 {
820 int dstIndex = *pActiveIndex;
821 int parentIndex = parentIndices[i];
822 pActiveIndex += incrIndex;
823
824 nw::math::VEC3 parentTransformedVelocity;
825 nw::math::MTX34 workMatrix;
826
827 nw::math::MTX34 particleMatrix;
828 nw::math::MTX34Identity(&particleMatrix);
829
830 nw::math::MTX34Translate(&particleMatrix, parentTranslate[parentIndex]);
831 ////nw::math::MTX34RotXYZRad(&workMatrix, rotate[index].x, rotate[index].y, rotate[index].z);
832 ////nw::math::MTX34Mult(&particleMatrix, particleMatrix, workMatrix);
833 ////nw::math::MTX34Scale(&workMatrix, scale[index]);
834 ////nw::math::MTX34Mult(&particleMatrix, particleMatrix, workMatrix);
835
836 nw::math::MTX34 transformMatrix;
837 nw::math::MTX34Mult(&transformMatrix, this->InverseWorldMatrix(), emitterMatrix);
838 nw::math::MTX34Mult(&workMatrix, transformMatrix, particleMatrix);
839
840 nw::math::VEC3Transform(&translate[dstIndex], workMatrix, translate[dstIndex]);
841
842 // 親からの継承処理
843 if (isInheritParentVelocity)
844 {
845 // 速度の継承
846 velocity[dstIndex] += parentVelocity[parentIndex];
847 nw::math::VEC3TransformNormal(&velocity[dstIndex], workMatrix, velocity[dstIndex]);
848
849 // NW4R相当の機能では、速度以外にスケール・ローテート・アルファ・カラーがあります。
850 }
851 else
852 {
853 nw::math::VEC3TransformNormal(&velocity[dstIndex], workMatrix, velocity[dstIndex]);
854 }
855 }
856 }
857 else
858 {
859 for (int i = 0; i < particleCount; ++i)
860 {
861 int dstIndex = *pActiveIndex;
862 int parentIndex = parentIndices[i];
863 pActiveIndex += incrIndex;
864
865 translate[dstIndex] += parentTranslate[parentIndex];
866
867 // 親からの継承処理
868 if (isInheritParentVelocity)
869 {
870 // 速度の継承
871 velocity[dstIndex] += parentVelocity[parentIndex];
872
873 // NW4R相当の機能では、速度以外にスケール・ローテート・アルファ・カラーがあります。
874 }
875 }
876 }
877 }
878 }
879 #else
880 //----------------------------------------
881 int
AddParticles(int particleCount)882 ParticleSet::AddParticles(
883 int particleCount
884 )
885 {
886 ParticleCollection* collection = this->GetParticleCollection();
887
888 const int collectionCount = collection->GetCount();
889 const int collectionCapacity = collection->GetCapacity();
890
891 int freeSize = collectionCapacity - collectionCount;
892 if (particleCount > freeSize)
893 {
894 particleCount = freeSize;
895 }
896
897 if (particleCount <= 0)
898 {
899 return 0;
900 }
901
902 const bool isAscendingOrder = this->IsAscendingOrder();
903
904 ParticleModel* model = static_cast<ParticleModel*>(this->GetParent());
905 NW_NULL_ASSERT(model);
906 ParticleTime time = model->ParticleAnimFrameController().GetFrame();
907
908 u16* activeIndex = (u16*)collection->GetStreamPtr(PARTICLEUSAGE_ACTIVEINDEX, PARTICLE_BUFFER_FRONT);
909 NW_NULL_ASSERT(activeIndex);
910
911 const int startIndex = (isAscendingOrder)? collection->GetCount() : collection->GetCapacity() - collection->GetCount() - 1;
912 const int incrIndex = (isAscendingOrder)? 1 : -1;
913
914 u16* pFreeIndex = (u16*)collection->GetStreamPtr(PARTICLEUSAGE_FREEINDEX, PARTICLE_BUFFER_FRONT);
915 NW_NULL_ASSERT(pFreeIndex);
916 pFreeIndex += collection->GetCapacity() - collection->GetCount() - 1;
917
918 ParticleTime* birth = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_BIRTH, PARTICLE_BUFFER_FRONT);
919 NW_NULL_ASSERT(birth);
920
921 nw::math::VEC3* velocity = (nw::math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_VELOCITY, PARTICLE_BUFFER_FRONT);
922 NW_NULL_ASSERT(velocity);
923
924 int minIndex = collection->GetMinActiveIndex();
925 int maxIndex = collection->GetMaxActiveIndex();
926
927 u16* pActiveIndex = activeIndex + startIndex;
928 for (int i = particleCount; i != 0; --i)
929 {
930 int nextIndex = *pFreeIndex;
931 --pFreeIndex;
932
933 *pActiveIndex = nextIndex;
934 pActiveIndex += incrIndex;
935
936 if (minIndex > nextIndex)
937 {
938 minIndex = nextIndex;
939 }
940
941 if (maxIndex < nextIndex)
942 {
943 maxIndex = nextIndex;
944 }
945
946 birth[nextIndex] = time;
947 velocity[nextIndex].x = 0.0f;
948 velocity[nextIndex].y = 0.0f;
949 velocity[nextIndex].z = 0.0f;
950 }
951
952 collection->SetMinActiveIndex(minIndex);
953 collection->SetMaxActiveIndex(maxIndex);
954
955 collection->SetCount(collectionCount + particleCount);
956
957 return particleCount;
958 }
959 #endif
960
961 //----------------------------------------
962 void
InitializeParticles(int startIndex,int count,int incrIndex,ParticleTime time)963 ParticleSet::InitializeParticles(
964 int startIndex,
965 int count,
966 int incrIndex,
967 ParticleTime time
968 )
969 {
970 ParticleCollection* collection = this->GetParticleCollection();
971
972 u16* activeIndex = (u16*)collection->GetStreamPtr(PARTICLEUSAGE_ACTIVEINDEX, PARTICLE_BUFFER_FRONT);
973 activeIndex += startIndex;
974
975 ut::MoveArray<Initializer>::iterator endIter = this->m_Initializers.end();
976 for (ut::MoveArray<Initializer>::iterator iter = this->m_Initializers.begin(); iter != endIter;)
977 {
978 Initializer& workInitializer = *iter++;
979 if (workInitializer.resource == NULL)
980 {
981 continue;
982 }
983
984 const ResParticleInitializer initializer(workInitializer.resource);
985
986 if (!initializer.GetInitializerEnabled())
987 {
988 continue;
989 }
990
991 void* targetStream;
992 if (collection->GetBufferSide())
993 {
994 targetStream = workInitializer.m_TargetStreams[1];
995 }
996 else
997 {
998 targetStream = workInitializer.m_TargetStreams[0];
999 }
1000
1001 switch (workInitializer.m_Type)
1002 {
1003 case ResParticleFloatImmediateInitializer::TYPE_INFO:
1004 {
1005 NW_PARTICLE_PROFILE_START("ParticleFloatImmediateInitializer");
1006
1007 ResParticleFloatImmediateInitializer floatInitializer(initializer.ptr());
1008 NW_ASSERT(floatInitializer.IsValid());
1009
1010 f32* storagePtr = (f32*)targetStream;
1011
1012 const f32 value = floatInitializer.GetImmediateValue();
1013
1014 u16* pActiveIndex = activeIndex;
1015 for (int j = count; j != 0; --j)
1016 {
1017 int index = *pActiveIndex;
1018 pActiveIndex += incrIndex;
1019
1020 storagePtr[index] = value;
1021 }
1022
1023 NW_PARTICLE_PROFILE_STOP();
1024 }
1025 break;
1026 case ResParticleVector2ImmediateInitializer::TYPE_INFO:
1027 {
1028 NW_PARTICLE_PROFILE_START("ParticleVector2ImmediateInitializer");
1029
1030 ResParticleVector2ImmediateInitializer vec2Initializer(initializer.ptr());
1031 NW_ASSERT(vec2Initializer.IsValid());
1032
1033 nw::math::VEC2* storagePtr = (nw::math::VEC2*)targetStream;
1034
1035 const nw::math::VEC2 value = vec2Initializer.GetImmediateValue();
1036
1037 u16* pActiveIndex = activeIndex;
1038 for (int j = count; j != 0; --j)
1039 {
1040 int index = *pActiveIndex;
1041 pActiveIndex += incrIndex;
1042
1043 storagePtr[index] = value;
1044 }
1045
1046 NW_PARTICLE_PROFILE_STOP();
1047 }
1048
1049 break;
1050 case ResParticleVector3ImmediateInitializer::TYPE_INFO:
1051 {
1052 NW_PARTICLE_PROFILE_START("ParticleVector3ImmediateInitializer");
1053
1054 ResParticleVector3ImmediateInitializer vec3Initializer(initializer.ptr());
1055 NW_ASSERT(vec3Initializer.IsValid());
1056
1057 nw::math::VEC3* storagePtr = (nw::math::VEC3*)targetStream;
1058 NW_NULL_ASSERT(storagePtr);
1059
1060 const nw::math::VEC3 value = vec3Initializer.GetImmediateValue();
1061
1062 u16* pActiveIndex = activeIndex;
1063 for (int j = count; j != 0; --j)
1064 {
1065 int index = *pActiveIndex;
1066 pActiveIndex += incrIndex;
1067
1068 storagePtr[index] = value;
1069 }
1070
1071 NW_PARTICLE_PROFILE_STOP();
1072 }
1073 break;
1074 case ResParticleFloatRandomInitializer::TYPE_INFO:
1075 {
1076 NW_PARTICLE_PROFILE_START("ParticleFloatRandomInitializer");
1077
1078 ResParticleFloatRandomInitializer floatInitializer(initializer.ptr());
1079 NW_ASSERT(floatInitializer.IsValid());
1080
1081 f32* storagePtr = (f32*)targetStream;
1082 NW_NULL_ASSERT(storagePtr);
1083
1084 const f32 baseValue = floatInitializer.GetBaseValue();
1085 const f32 random = floatInitializer.GetRandom();
1086
1087 u16* pActiveIndex = activeIndex;
1088 for (int j = count; j != 0; --j)
1089 {
1090 int index = *pActiveIndex;
1091 pActiveIndex += incrIndex;
1092
1093 f32 value = baseValue;
1094
1095 if (random != 0.0f)
1096 {
1097 value += value * this->m_ParticleRandom.NextFloatSignedOne() * random;
1098 }
1099
1100 storagePtr[index] = value;
1101 }
1102
1103 NW_PARTICLE_PROFILE_STOP();
1104 }
1105 break;
1106 case ResParticleDirectionalVelocityInitializer::TYPE_INFO:
1107 {
1108 NW_PARTICLE_PROFILE_START("ParticleDirectionalVelocityInitializer");
1109
1110 ResParticleDirectionalVelocityInitializer velInitializer(initializer.ptr());
1111 NW_ASSERT(velInitializer.IsValid());
1112
1113 nw::math::VEC3* storagePtr = (nw::math::VEC3*)targetStream;
1114 NW_NULL_ASSERT(storagePtr);
1115
1116 const nw::math::VEC3 direction = velInitializer.GetDirection();
1117 const f32 power = velInitializer.GetPower();
1118
1119 nw::math::VEC3 velocity = direction;
1120 velocity *= power;
1121
1122 u16* pActiveIndex = activeIndex;
1123 for (int j = count; j != 0; --j)
1124 {
1125 int index = *pActiveIndex;
1126 pActiveIndex += incrIndex;
1127
1128 storagePtr[index] += velocity;
1129 }
1130
1131 NW_PARTICLE_PROFILE_STOP();
1132 }
1133 break;
1134 case ResParticleRandomDirectionalVelocityInitializer::TYPE_INFO:
1135 {
1136 #define PARTICLE_CUSTUM_EPSILON 0.0001f
1137
1138 NW_PARTICLE_PROFILE_START("ParticleRandomDirectionalVelocityInitializer");
1139
1140 ResParticleRandomDirectionalVelocityInitializer velInitializer(initializer.ptr());
1141 NW_ASSERT(velInitializer.IsValid());
1142
1143 nw::math::VEC3* storagePtr = (nw::math::VEC3*)targetStream;
1144 NW_NULL_ASSERT(storagePtr);
1145
1146 nw::math::VEC3 direction = velInitializer.GetDirection();
1147 // direction が不正な場合は処理を行わない
1148 if (direction.x == 0.f && direction.y == 0.f && direction.z == 0.f )
1149 {
1150 break;
1151 }
1152
1153 const f32 power = velInitializer.GetPower();
1154 const f32 angle = velInitializer.GetAngle();
1155 f32 sin, cos;
1156
1157 // 変換行列のパラメータを計算
1158 f32 sx = direction.z;
1159 NW_ASSERT(sx <= 1.0f);
1160
1161 f32 cx = 1.0f - sx * sx;
1162 f32 divcx;
1163 if (cx >= PARTICLE_CUSTUM_EPSILON)
1164 {
1165 divcx = math::FrSqrt(cx);
1166 }
1167 else
1168 {
1169 // 誤差等の対策
1170 divcx = 0.0f;
1171 }
1172 cx = cx * divcx;
1173
1174 f32 sz;
1175 f32 cz;
1176
1177 if (cx == 0.0f)
1178 {
1179 sz = 0.0f;
1180 cz = 1.0f;
1181 }
1182 else
1183 {
1184 sz = direction.x * -divcx;
1185 cz = direction.y * divcx;
1186 }
1187
1188 f32 sxsz = sx * sz;
1189 f32 msxcz = -sx * cz;
1190
1191 // (0,1,0)を基準にランダムを適用した円錐状ベクトルを生成する
1192 nw::math::VEC3 velocity;
1193
1194 u16* pActiveIndex = activeIndex;
1195
1196 for (int j = count; j != 0; --j)
1197 {
1198 nn::math::SinCosRad(&sin, &cos, nw::math::F_PI/2.f - (angle * this->m_ParticleRandom.NextFloatSignedOne()));
1199 velocity.x = cos;
1200 velocity.y = sin;
1201 velocity.z = 0.0f;
1202
1203 f32 rad = velocity.x;
1204 nn::math::SinCosRad(&sin, &cos, 2.0f * nw::math::F_PI * this->m_ParticleRandom.NextFloatSignedOne());
1205 velocity.x = rad * cos;
1206 velocity.z = rad * sin;
1207
1208 nw::math::VEC3 tempv;
1209
1210 tempv.x =
1211 velocity.x * cz +
1212 velocity.y * direction.x +
1213 velocity.z * sxsz;
1214
1215 tempv.y =
1216 velocity.x * sz +
1217 velocity.y * direction.y +
1218 velocity.z * msxcz;
1219
1220 tempv.z =
1221 velocity.y * direction.z +
1222 velocity.z * cx;
1223
1224 velocity = tempv * power;
1225
1226 int index = *pActiveIndex;
1227 pActiveIndex += incrIndex;
1228
1229 storagePtr[index] += velocity;
1230 }
1231
1232 NW_PARTICLE_PROFILE_STOP();
1233 }
1234 break;
1235 case ResParticleOriginVelocityInitializer::TYPE_INFO:
1236 {
1237 NW_PARTICLE_PROFILE_START("ParticleOriginVelocityInitializer");
1238
1239 ResParticleOriginVelocityInitializer velInitializer(initializer.ptr());
1240 NW_ASSERT(velInitializer.IsValid());
1241
1242 nw::math::VEC3* storagePtr = (nw::math::VEC3*)targetStream;
1243 NW_NULL_ASSERT(storagePtr);
1244
1245 nw::math::VEC3* trans = (nw::math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_TRANSLATE, PARTICLE_BUFFER_FRONT);
1246 NW_NULL_ASSERT(trans);
1247
1248 const f32 power = velInitializer.GetPower();
1249
1250 u16* pActiveIndex = activeIndex;
1251 for (int j = count; j != 0; --j)
1252 {
1253 int index = *pActiveIndex;
1254 pActiveIndex += incrIndex;
1255
1256 nw::math::VEC3 velocity = trans[index];
1257 velocity.SafeNormalize(nw::math::VEC3(0, 0, 0));
1258 velocity *= power;
1259
1260 storagePtr[index] += velocity;
1261 }
1262
1263 NW_PARTICLE_PROFILE_STOP();
1264 }
1265 break;
1266 case ResParticleRandomVelocityInitializer::TYPE_INFO:
1267 {
1268 NW_PARTICLE_PROFILE_START("ParticleRandomVelocityInitializer");
1269
1270 ResParticleRandomVelocityInitializer velInitializer(initializer.ptr());
1271 NW_ASSERT(velInitializer.IsValid());
1272
1273 nw::math::VEC3* storagePtr = (nw::math::VEC3*)targetStream;
1274 NW_NULL_ASSERT(storagePtr);
1275
1276 const f32 power = velInitializer.GetPower();
1277
1278 u16* pActiveIndex = activeIndex;
1279 for (int j = count; j != 0; --j)
1280 {
1281 int index = *pActiveIndex;
1282 pActiveIndex += incrIndex;
1283
1284 nw::math::VEC3 velocity(0.0f, 0.0f, 0.0f);
1285
1286 f32 yaw = this->m_ParticleRandom.NextFloat() * 2.0f * nw::math::F_PI;
1287 f32 pitch = this->m_ParticleRandom.NextFloatSignedHalf() * nw::math::F_PI;
1288
1289 f32 sinYaw = 0.0f;
1290 f32 cosYaw = 1.0f;
1291 if (yaw != 0.0f)
1292 {
1293 nw::math::SinCosRad(&sinYaw, &cosYaw, yaw);
1294 }
1295
1296 f32 sinPitch = 0.0f;
1297 f32 cosPitch = 1.0f;
1298 if (pitch != 0.0f)
1299 {
1300 nw::math::SinCosRad(&sinPitch, &cosPitch, pitch);
1301 }
1302
1303 velocity.x = sinYaw * -cosPitch;
1304 velocity.y = -sinPitch;
1305 velocity.z = cosYaw * cosPitch;
1306
1307 velocity *= power;
1308
1309 storagePtr[index] += velocity;
1310 }
1311
1312 NW_PARTICLE_PROFILE_STOP();
1313 }
1314 break;
1315 case ResParticleYAxisVelocityInitializer::TYPE_INFO:
1316 {
1317 NW_PARTICLE_PROFILE_START("ParticleYAxisVelocityInitializer");
1318
1319 ResParticleYAxisVelocityInitializer velInitializer(initializer.ptr());
1320 NW_ASSERT(velInitializer.IsValid());
1321
1322 nw::math::VEC3* storagePtr = (nw::math::VEC3*)targetStream;
1323 NW_NULL_ASSERT(storagePtr);
1324
1325 nw::math::VEC3* trans = (nw::math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_TRANSLATE, PARTICLE_BUFFER_FRONT);
1326
1327 const f32 power = velInitializer.GetPower();
1328
1329 u16* pActiveIndex = activeIndex;
1330 for (int j = count; j != 0; --j)
1331 {
1332 int index = *pActiveIndex;
1333 pActiveIndex += incrIndex;
1334
1335 nw::math::VEC3 velocity(trans[index].x, 0.0f, trans[index].z);
1336 velocity.SafeNormalize(nw::math::VEC3(0, 0, 0));
1337 velocity *= power;
1338
1339 storagePtr[index] += velocity;
1340 }
1341
1342 NW_PARTICLE_PROFILE_STOP();
1343 }
1344 break;
1345 case ResParticleVector3Random1Initializer::TYPE_INFO:
1346 {
1347 NW_PARTICLE_PROFILE_START("ParticleVector3Random1Initializer");
1348
1349 ResParticleVector3Random1Initializer vec3Initializer(initializer.ptr());
1350 NW_ASSERT(vec3Initializer.IsValid());
1351
1352 nw::math::VEC3* storagePtr = (nw::math::VEC3*)targetStream;
1353 NW_NULL_ASSERT(storagePtr);
1354
1355 const nw::math::VEC3 baseValue = vec3Initializer.GetBaseValue();
1356 const f32 random = vec3Initializer.GetRandom();
1357
1358 u16* pActiveIndex = activeIndex;
1359 for (int j = count; j != 0; --j)
1360 {
1361 int index = *pActiveIndex;
1362 pActiveIndex += incrIndex;
1363
1364 nw::math::VEC3 value(baseValue);
1365
1366 if (random != 0.0f)
1367 {
1368 f32 floatRandom = this->m_ParticleRandom.NextFloatSignedOne();
1369 floatRandom *= random;
1370
1371 value.x += value.x * floatRandom;
1372 value.y += value.y * floatRandom;
1373 value.z += value.z * floatRandom;
1374 }
1375
1376 storagePtr[index] = value;
1377 }
1378
1379 NW_PARTICLE_PROFILE_STOP();
1380 }
1381 break;
1382 case ResParticleVector3Random3Initializer::TYPE_INFO:
1383 {
1384 NW_PARTICLE_PROFILE_START("ParticleVector3Random3Initializer");
1385
1386 ResParticleVector3Random3Initializer vec3Initializer(initializer.ptr());
1387 NW_ASSERT(vec3Initializer.IsValid());
1388
1389 nw::math::VEC3* storagePtr = (nw::math::VEC3*)targetStream;
1390 NW_NULL_ASSERT(storagePtr);
1391
1392 const nw::math::VEC3 baseValue = vec3Initializer.GetBaseValue();
1393 const nw::math::VEC3 random = vec3Initializer.GetRandom();
1394
1395 u16* pActiveIndex = activeIndex;
1396 for (int j = count; j != 0; --j)
1397 {
1398 int index = *pActiveIndex;
1399 pActiveIndex += incrIndex;
1400
1401 nw::math::VEC3 value(baseValue);
1402
1403 if (random.x != 0.0f)
1404 {
1405 f32 floatRandom = this->m_ParticleRandom.NextFloatSignedOne();
1406 floatRandom *= random.x;
1407 value.x += value.x * floatRandom;
1408 }
1409
1410 if (random.y != 0.0f)
1411 {
1412 f32 floatRandom = this->m_ParticleRandom.NextFloatSignedOne();
1413 floatRandom *= random.y;
1414 value.y += value.y * floatRandom;
1415 }
1416
1417 if (random.z != 0.0f)
1418 {
1419 f32 floatRandom = this->m_ParticleRandom.NextFloatSignedOne();
1420 floatRandom *= random.z;
1421 value.z += value.z * floatRandom;
1422 }
1423
1424 storagePtr[index] = value;
1425 }
1426
1427 NW_PARTICLE_PROFILE_STOP();
1428 }
1429 break;
1430 case ResParticleVector3MultRandomInitializer::TYPE_INFO:
1431 {
1432 NW_PARTICLE_PROFILE_START("ParticleVector3MultRandomInitializer");
1433
1434 ResParticleVector3MultRandomInitializer vec3Initializer(initializer.ptr());
1435 NW_ASSERT(vec3Initializer.IsValid());
1436
1437 nw::math::VEC3* storagePtr = (nw::math::VEC3*)targetStream;
1438 NW_NULL_ASSERT(storagePtr);
1439
1440 const f32 random = vec3Initializer.GetRandom();
1441
1442 u16* pActiveIndex = activeIndex;
1443 for (int j = count; j != 0; --j)
1444 {
1445 int index = *pActiveIndex;
1446 pActiveIndex += incrIndex;
1447
1448 f32 floatRandom = this->m_ParticleRandom.NextFloatSignedOne();
1449 floatRandom = 1.0f + (floatRandom * random);
1450
1451 storagePtr[index].x *= floatRandom;
1452 storagePtr[index].y *= floatRandom;
1453 storagePtr[index].z *= floatRandom;
1454 }
1455
1456 NW_PARTICLE_PROFILE_STOP();
1457 }
1458 break;
1459 case ResParticleFloatRangeRandomInitializer::TYPE_INFO:
1460 {
1461 NW_PARTICLE_PROFILE_START("ParticleFloatRangeRandomInitializer");
1462
1463 ResParticleFloatRangeRandomInitializer floatInitializer(initializer.ptr());
1464 NW_ASSERT(floatInitializer.IsValid());
1465
1466 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
1467 ParticleTime* limit = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_NEG_TIMELIMIT, PARTICLE_BUFFER_FRONT);
1468 #endif
1469
1470 ParticleTime* storagePtr = (ParticleTime*)targetStream;
1471 NW_NULL_ASSERT(storagePtr);
1472
1473 f32 minValue = floatInitializer.GetMinValue();
1474 f32 maxValue = floatInitializer.GetMaxValue();
1475
1476 #if 1
1477 NW_ASSERT(minValue <= maxValue);
1478 #else
1479 if (minValue > maxValue)
1480 {
1481 f32 swap = minValue;
1482 minValue = maxValue;
1483 maxValue = swap;
1484 }
1485 #endif
1486
1487 f32 diff = maxValue - minValue;
1488
1489 u16* pActiveIndex = activeIndex;
1490 for (int j = count; j != 0; --j)
1491 {
1492 int index = *pActiveIndex;
1493 pActiveIndex += incrIndex;
1494
1495 f32 value = minValue + this->m_ParticleRandom.Next((int)(diff + 1));
1496
1497 storagePtr[index] = value;
1498 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
1499 limit[index] = -(time + value);
1500 #endif
1501 }
1502
1503 NW_PARTICLE_PROFILE_STOP();
1504 }
1505 break;
1506 }
1507 }
1508
1509 }
1510
1511
1512 //----------------------------------------
1513 //! @brief アニメーションで参照すべきテーブルの位置を計算します。
1514 //! @details :private
1515 //!
1516 //! @param[in] animationOption アニメーションオプションです。
1517 //! @param[in] id パーティクルのIDです。
1518 //! @param[in] birth パーティクルの生まれた時刻です。
1519 //! @param[in] life パーティクルの寿命です。
1520 //! @param[in] time 現在の時刻です。
1521 //! @param[in] animationLength アニメーションの長さです。
1522 //! @param[out] interp 次のキーと補間すべきかのフラグです。
1523 //! @param[out] interpFactor 次のキーと補間する際の係数です。0で補間なしです。
1524 //! @param[out] updaterIndex アップデータのインデックス番号です。
1525 //!
1526 // CALC_OPT:inline 指定へ。
1527 /*static*/inline int
CalcAnimationDataIndex(const ResParticleAnimationOption & animationOption,u32 id,ParticleTime birth,ParticleTime life,ParticleTime time,int animationLength,bool * interp,f32 * interpFactor,int updaterIndex)1528 CalcAnimationDataIndex(
1529 const ResParticleAnimationOption& animationOption,
1530 u32 id,
1531 ParticleTime birth,
1532 ParticleTime life,
1533 ParticleTime time,
1534 int animationLength,
1535 bool* interp,
1536 f32* interpFactor,
1537 int updaterIndex
1538 )
1539 {
1540 NW_NULL_ASSERT(interp);
1541 NW_NULL_ASSERT(interpFactor);
1542
1543 ParticleTime ptclTime = animationOption.EvaluateAnimationFrame(id, birth, life, time, updaterIndex);
1544
1545 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
1546 if (ptclTime < 0)
1547 #else
1548 if (ptclTime.GetParticleTimeValue() < 0)
1549 #endif
1550 {
1551 ptclTime = 0;
1552 }
1553
1554 if (ptclTime >= animationLength)
1555 {
1556 ptclTime = (f32)animationLength - 1;
1557 }
1558
1559 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
1560 int index = (int)ptclTime;
1561 *interpFactor = ptclTime - index;
1562 #else
1563 int index = ptclTime.GetIntegralParts();
1564 *interpFactor = ptclTime.GetFractionalParts();
1565 #endif
1566
1567 if (*interpFactor != 0.0f)
1568 {
1569 *interp = true;
1570 }
1571 else
1572 {
1573 *interp = false;
1574 }
1575
1576 return index;
1577 }
1578
ParticleAccelarationUpdater(const ResParticleUpdaterData * updater,ParticleCollection * collection,f32 diffTime)1579 void ParticleAccelarationUpdater(
1580 const ResParticleUpdaterData* updater,
1581 ParticleCollection* collection,
1582 f32 diffTime
1583 )
1584 {
1585 NW_PARTICLE_PROFILE_START("ParticleAccelarationUpdater");
1586
1587 ResParticleAccelarationUpdater accelUpdater(updater);
1588 NW_ASSERT(accelUpdater.IsValid());
1589
1590 ParticleUsage target = accelUpdater.GetTargetStream();
1591 nw::math::VEC3* storagePtr = (nw::math::VEC3*)collection->GetStreamPtr(target, PARTICLE_BUFFER_FRONT);
1592 NW_NULL_ASSERT(storagePtr);
1593
1594 // factor = 1 - a として
1595 // 1 - difftime * a と近似します(粗めです)。
1596 f32 factor = 1.0f - diffTime * (1.0f - accelUpdater.GetFactor());
1597
1598 const u32 maxIndex = collection->GetMaxActiveIndex() + 1;
1599 for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index)
1600 {
1601 storagePtr[index] *= factor;
1602 }
1603
1604 NW_PARTICLE_PROFILE_STOP();
1605 }
1606
ParticleFloatImmediateUpdater(const ResParticleUpdaterData * updater,ParticleCollection * collection,ParticleTime time,int updaterIndex)1607 void ParticleFloatImmediateUpdater(
1608 const ResParticleUpdaterData* updater,
1609 ParticleCollection* collection,
1610 ParticleTime time,
1611 int updaterIndex
1612 )
1613 {
1614 NW_PARTICLE_PROFILE_START("ParticleFloatImmediateUpdater");
1615
1616 ParticleTime* birth = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_BIRTH, PARTICLE_BUFFER_FRONT);
1617
1618 ParticleTime* life;
1619 ParticleTime lifeParam = 1.0f;
1620 collection->GetStreamOrParameter(PARTICLEUSAGE_LIFE, &life, &lifeParam, PARTICLE_BUFFER_FRONT);
1621
1622 int animationLength = 0;
1623 //int animationDimension = 0;
1624 //int animationStride = 0;
1625 bool* animationEnable = NULL;
1626 f32* animationData = NULL;
1627
1628 ResParticleFloatImmediateUpdater floatUpdater(updater);
1629 NW_ASSERT(floatUpdater.IsValid());
1630
1631 ParticleUsage target = floatUpdater.GetTargetStream();
1632 f32* storagePtr = (f32*)collection->GetStreamPtr(target, PARTICLE_BUFFER_FRONT);
1633 NW_NULL_ASSERT(storagePtr);
1634
1635 ResParticleAnimation animation = floatUpdater.GetParticleAnimation();
1636 if (animation.IsValid())
1637 {
1638 animationLength = animation.GetAnimationLength();
1639 //animationDimension = animation.GetAnimationDimension();
1640 //animationStride = animation.GetAnimationStride();
1641 animationEnable = animation.GetAnimationEnabled();
1642 animationData = animation.GetAnimationData();
1643 }
1644
1645 if (animationData == NULL || !animationEnable[0])
1646 {
1647 const f32 baseValue = floatUpdater.GetDefaultValue();
1648
1649 const u32 maxIndex = collection->GetMaxActiveIndex() + 1;
1650 for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index)
1651 {
1652 storagePtr[index] = baseValue;
1653 }
1654 }
1655 else
1656 {
1657 const u32 maxIndex = collection->GetMaxActiveIndex() + 1;
1658 for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index)
1659 {
1660 bool interp;
1661 f32 interpFactor;
1662 int animationDataIndex;
1663
1664 ResParticleAnimationOption animationOption = animation.GetParticleAnimationOption();
1665
1666 animationDataIndex = CalcAnimationDataIndex(
1667 animationOption,
1668 index,
1669 birth[index],
1670 (life == NULL) ? lifeParam : life[index],
1671 time,
1672 animationLength,
1673 &interp,
1674 &interpFactor,
1675 updaterIndex);
1676 //animationDataIndex *= animationStride;
1677
1678 f32 value = animationData[animationDataIndex++];
1679
1680 if (interp)
1681 {
1682 f32 nextValue = animationData[animationDataIndex++];
1683 value = value + (nextValue - value) * interpFactor;
1684 }
1685
1686 storagePtr[index] = value;
1687 }
1688 }
1689
1690 NW_PARTICLE_PROFILE_STOP();
1691 }
1692
ParticleFloatImmediate4KeyUpdater(const ResParticleUpdaterData * updater,ParticleCollection * collection,ParticleTime time)1693 void ParticleFloatImmediate4KeyUpdater(
1694 const ResParticleUpdaterData* updater,
1695 ParticleCollection* collection,
1696 ParticleTime time
1697 )
1698 {
1699 NW_PARTICLE_PROFILE_START("ParticleFloatImmediate4KeyUpdater");
1700
1701 ParticleTime* birth = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_BIRTH, PARTICLE_BUFFER_FRONT);
1702
1703 ParticleTime* life;
1704 ParticleTime lifeParam = 1.0f;
1705 collection->GetStreamOrParameter(PARTICLEUSAGE_LIFE, &life, &lifeParam, PARTICLE_BUFFER_FRONT);
1706
1707 ResParticleFloatImmediate4KeyUpdater fkeyUpdater(updater);
1708 NW_ASSERT(fkeyUpdater.IsValid());
1709
1710 ParticleUsage target = fkeyUpdater.GetTargetStream();
1711 f32* storagePtr = (f32*)collection->GetStreamPtr(target, PARTICLE_BUFFER_FRONT);
1712 NW_NULL_ASSERT(storagePtr);
1713
1714 register u32 inTime = fkeyUpdater.GetInTime();
1715 register u32 outTime = fkeyUpdater.GetOutTime();
1716 register s32 value0 = fkeyUpdater.GetValue0();
1717 register s32 value1 = fkeyUpdater.GetValue1();
1718 register s32 value2 = fkeyUpdater.GetValue2();
1719 register s32 value3 = fkeyUpdater.GetValue3();
1720 register s32 value4 = fkeyUpdater.GetValue4();
1721
1722 if (life == NULL)
1723 {
1724 const u32 maxIndex = collection->GetMaxActiveIndex() + 1;
1725 for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index)
1726 {
1727 ParticleTime difftime = time - birth[index];
1728 ParticleTime ftime = (ParticleTime)(difftime / lifeParam);
1729 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
1730 u32 utime = (u32)(ftime * 0x10000);
1731 #else
1732 u32 utime = (u32)(ftime.GetS32Value() * 0x10000);
1733 #endif
1734
1735 u32 value;
1736
1737 if (utime < inTime)
1738 {
1739 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
1740 value = value2 * difftime / lifeParam + value0;
1741 #else
1742 value = value2 * ftime.GetFloat32Value() + value0;
1743 #endif
1744 }
1745 else if (utime < outTime)
1746 {
1747 value = value1;
1748 }
1749 else
1750 {
1751 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
1752 value = value3 * ftime + value4;
1753 #else
1754 value = value3 * ftime.GetFloat32Value() + value4;
1755 #endif
1756 }
1757
1758 storagePtr[index] = (f32)value / 65535.f;
1759 }
1760 }
1761 else
1762 {
1763 const u32 maxIndex = collection->GetMaxActiveIndex() + 1;
1764 for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index)
1765 {
1766 ParticleTime difftime = time - birth[index];
1767 ParticleTime ftime = (ParticleTime)(difftime / life[index]);
1768 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
1769 u32 utime = (u32)(ftime * 0x10000);
1770 #else
1771 u32 utime = (u32)(ftime.GetS32Value() * 0x10000);
1772 #endif
1773
1774 u32 value;
1775
1776 if (utime < inTime)
1777 {
1778 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
1779 value = value2 * difftime / life[index] + value0;
1780 #else
1781 value = value2 * ftime.GetFloat32Value() + value0;
1782 #endif
1783 }
1784 else if (utime < outTime)
1785 {
1786 value = value1;
1787 }
1788 else
1789 {
1790 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
1791 value = value3 * ftime + value4;
1792 #else
1793 value = value3 * ftime.GetFloat32Value() + value4;
1794 #endif
1795 }
1796
1797 storagePtr[index] = (f32)value / 65535.f;
1798 }
1799 }
1800
1801 NW_PARTICLE_PROFILE_STOP();
1802 }
1803
ParticleVector3Immediate4KeyUpdater(const ResParticleUpdaterData * updater,ParticleCollection * collection,ParticleTime time)1804 void ParticleVector3Immediate4KeyUpdater(
1805 const ResParticleUpdaterData* updater,
1806 ParticleCollection* collection,
1807 ParticleTime time
1808 )
1809 {
1810 NW_PARTICLE_PROFILE_START("ParticleVector3Immediate4KeyUpdater");
1811
1812 ParticleTime* birth = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_BIRTH, PARTICLE_BUFFER_FRONT);
1813
1814 ParticleTime* life;
1815 ParticleTime lifeParam = 1.0f;
1816 collection->GetStreamOrParameter(PARTICLEUSAGE_LIFE, &life, &lifeParam, PARTICLE_BUFFER_FRONT);
1817
1818 ResParticleVector3Immediate4KeyUpdater fkeyUpdater(updater);
1819 NW_ASSERT(fkeyUpdater.IsValid());
1820
1821 ParticleUsage target = fkeyUpdater.GetTargetStream();
1822 math::VEC3* storagePtr = (math::VEC3*)collection->GetStreamPtr(target, PARTICLE_BUFFER_FRONT);
1823 NW_NULL_ASSERT(storagePtr);
1824
1825 register u32 inTime = fkeyUpdater.GetInTime();
1826 register u32 outTime = fkeyUpdater.GetOutTime();
1827 math::VEC3 value0 = fkeyUpdater.GetValue0();
1828 math::VEC3 value1 = fkeyUpdater.GetValue1();
1829 math::VEC3 value2 = fkeyUpdater.GetValue2();
1830 math::VEC3 value3 = fkeyUpdater.GetValue3();
1831 math::VEC3 value4 = fkeyUpdater.GetValue4();
1832
1833 if (life == NULL)
1834 {
1835 const u32 maxIndex = collection->GetMaxActiveIndex() + 1;
1836 for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index)
1837 {
1838 ParticleTime difftime = time - birth[index];
1839 ParticleTime ftime = (ParticleTime)(difftime / lifeParam);
1840 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
1841 u32 utime = (u32)(ftime * 0x10000);
1842 #else
1843 u32 utime = (u32)(ftime.GetFloat32Value() * 0x10000);
1844 #endif
1845
1846 math::VEC3 value;
1847
1848 if (utime < inTime)
1849 {
1850 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
1851 value = value2 * difftime / lifeParam + value0;
1852 #else
1853 value = value2 * ftime.GetFloat32Value() + value0;
1854 #endif
1855 }
1856 else if (utime < outTime)
1857 {
1858 value = value1;
1859 }
1860 else
1861 {
1862 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
1863 value = value3 * ftime + value4;
1864 #else
1865 value = value3 * ftime.GetFloat32Value() + value4;
1866 #endif
1867 }
1868
1869 storagePtr[index] = value;
1870 }
1871 }
1872 else
1873 {
1874 const u32 maxIndex = collection->GetMaxActiveIndex() + 1;
1875 for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index)
1876 {
1877 ParticleTime difftime = time - birth[index];
1878 ParticleTime ftime = (ParticleTime)(difftime / life[index]);
1879 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
1880
1881 u32 utime = (u32)(ftime * 0x10000);
1882 #else
1883 u32 utime = (u32)(ftime.GetFloat32Value() * 0x10000);
1884 #endif
1885
1886 math::VEC3 value;
1887
1888 if (utime < inTime)
1889 {
1890 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
1891 value = value2 * difftime / life[index] + value0;
1892 #else
1893 value = value2 * ftime.GetFloat32Value() + value0;
1894 #endif
1895 }
1896 else if (utime < outTime)
1897 {
1898 value = value1;
1899 }
1900 else
1901 {
1902 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
1903 value = value3 * ftime + value4;
1904 #else
1905 value = value3 * ftime.GetFloat32Value() + value4;
1906 #endif
1907 }
1908
1909 storagePtr[index] = value;
1910 }
1911 }
1912
1913 NW_PARTICLE_PROFILE_STOP();
1914 }
1915
ParticleGravityUpdater(nw::math::VEC3 * storagePtr,const ResParticleUpdaterData * updater,ParticleCollection * collection,f32 diffTime)1916 void ParticleGravityUpdater(
1917 nw::math::VEC3* storagePtr,
1918 const ResParticleUpdaterData* updater,
1919 ParticleCollection* collection,
1920 f32 diffTime
1921 )
1922 {
1923 NW_PARTICLE_PROFILE_START("ParticleGravityUpdater");
1924
1925 NW_NULL_ASSERT(storagePtr);
1926
1927 ResParticleGravityUpdater gravUpdater(updater);
1928 NW_ASSERT(gravUpdater.IsValid());
1929
1930 nw::math::VEC3 velocity = gravUpdater.GetDirection();
1931 const f32 power = gravUpdater.GetPower() * diffTime;
1932
1933 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
1934 velocity.SafeNormalize(nw::math::VEC3(0, -1, 0));
1935 #endif
1936 velocity *= power;
1937
1938 // 存在しないパーティクルもかまわず処理することで、スループットを上げる
1939 storagePtr += collection->GetMinActiveIndex();
1940 const int count = collection->GetMaxActiveIndex() - collection->GetMinActiveIndex() + 1;
1941 internal::VEC3ArrayAddScalar(storagePtr, &velocity, count);
1942
1943 NW_PARTICLE_PROFILE_STOP();
1944 }
1945
ParticleSpinUpdater(const ResParticleUpdaterData * updater,ParticleCollection * collection,f32 diffTime)1946 void ParticleSpinUpdater(
1947 const ResParticleUpdaterData* updater,
1948 ParticleCollection* collection,
1949 f32 diffTime
1950 )
1951 {
1952 NW_PARTICLE_PROFILE_START("ParticleSpinUpdater");
1953
1954 ResParticleSpinUpdater spinUpdater(updater);
1955 NW_ASSERT(spinUpdater.IsValid());
1956
1957 ParticleUsage target = spinUpdater.GetTargetStream();
1958 nw::math::VEC3* storagePtr = (nw::math::VEC3*)collection->GetStreamPtr(target, PARTICLE_BUFFER_FRONT);
1959 NW_NULL_ASSERT(storagePtr);
1960
1961 const nw::math::VEC3 axis = spinUpdater.GetAxis();
1962 const f32 power = spinUpdater.GetPower() * diffTime;
1963
1964 if (power != 0.0f && !axis.IsZero())
1965 {
1966 nw::math::MTX34 matrix;
1967 nw::math::MTX34RotAxisRad(&matrix, &axis, power);
1968
1969 nw::math::VEC3 value;
1970
1971 const u32 maxIndex = collection->GetMaxActiveIndex() + 1;
1972 for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index)
1973 {
1974 nw::math::VEC3TransformNormal(&value, matrix, storagePtr[index]);
1975 // CALC_OPT:展開することで、valueが確定する前からコピーが始まる。
1976 // storagePtr[index] = value;
1977 storagePtr[index].x = value.x;
1978 storagePtr[index].y = value.y;
1979 storagePtr[index].z = value.z;
1980 }
1981 }
1982
1983 NW_PARTICLE_PROFILE_STOP();
1984 }
1985
ParticleRandomUpdater(const ResParticleUpdaterData * updater,ParticleCollection * collection,ParticleTime time,ParticleTime prevTime,ParticleRandom & random)1986 void ParticleRandomUpdater(
1987 const ResParticleUpdaterData* updater,
1988 ParticleCollection* collection,
1989 ParticleTime time,
1990 ParticleTime prevTime,
1991 ParticleRandom& random
1992 )
1993 {
1994 NW_PARTICLE_PROFILE_START("ParticleRandomUpdater");
1995
1996 // 時間経過が無ければ処理をしない。
1997 if (time == prevTime) return;
1998
1999 ParticleTime* birth = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_BIRTH, PARTICLE_BUFFER_FRONT);
2000
2001 ParticleTime* life;
2002 ParticleTime lifeParam = 1.0f;
2003 collection->GetStreamOrParameter(PARTICLEUSAGE_LIFE, &life, &lifeParam, PARTICLE_BUFFER_FRONT);
2004
2005 ResParticleRandomUpdater randomUpdater(updater);
2006 NW_ASSERT(randomUpdater.IsValid());
2007
2008 ParticleUsage target = randomUpdater.GetTargetStream();
2009 nw::math::VEC3* storagePtr = (nw::math::VEC3*)collection->GetStreamPtr(target, PARTICLE_BUFFER_FRONT);
2010 NW_NULL_ASSERT(storagePtr);
2011
2012 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
2013 f32 processCount = math::FFloor(time) - math::FCeil(prevTime) + 1;
2014 #else
2015 f32 processCount = time.Floor() - prevTime.Ceil() + 1;
2016 #endif
2017 if (processCount > 0) // 整数を跨いでいないならいずれにしろ実行することはない
2018 {
2019 const s32 interval = randomUpdater.GetInterval();
2020 if (interval <= 1) // 毎フレーム
2021 {
2022 const nw::math::VEC3 power = randomUpdater.GetPower() * processCount;
2023
2024 const u32 maxIndex = collection->GetMaxActiveIndex() + 1;
2025 for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index)
2026 {
2027 storagePtr[index].x += power.x * random.NextFloatSignedOne();
2028 storagePtr[index].y += power.y * random.NextFloatSignedOne();
2029 storagePtr[index].z += power.z * random.NextFloatSignedOne();
2030 }
2031 }
2032 else // インターバルあり
2033 {
2034 const nw::math::VEC3 power = randomUpdater.GetPower() * processCount;
2035
2036 nw::math::VEC3 value;
2037
2038 const u32 maxIndex = collection->GetMaxActiveIndex() + 1;
2039 for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index)
2040 {
2041 // 粒子ごとに再計算
2042 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
2043 processCount =
2044 math::FFloor((time - birth[index]) / interval)
2045 - math::FCeil((prevTime - birth[index]) / interval)
2046 + 1;
2047 #else
2048 ParticleTime time_birth = time - birth[index];
2049 ParticleTime prev_birth = prevTime - birth[index];
2050 processCount =
2051 math::FFloor(time_birth.GetFloat32Value() / interval)
2052 - math::FCeil(prev_birth.GetFloat32Value() / interval)
2053 + 1;
2054 #endif
2055
2056 if (processCount > 0)
2057 {
2058 storagePtr[index].x += power.x * random.NextFloatSignedOne() * processCount;
2059 storagePtr[index].y += power.y * random.NextFloatSignedOne() * processCount;
2060 storagePtr[index].z += power.z * random.NextFloatSignedOne() * processCount;
2061 }
2062 }
2063 }
2064 }
2065
2066 NW_PARTICLE_PROFILE_STOP();
2067 }
2068
ParticleVector2ImmediateUpdater(const ResParticleUpdaterData * updater,ParticleCollection * collection,ParticleTime time,int updaterIndex)2069 void ParticleVector2ImmediateUpdater(
2070 const ResParticleUpdaterData* updater,
2071 ParticleCollection* collection,
2072 ParticleTime time,
2073 int updaterIndex
2074 )
2075 {
2076 NW_PARTICLE_PROFILE_START("ParticleVector2ImmediateUpdater");
2077
2078 ParticleTime* birth = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_BIRTH, PARTICLE_BUFFER_FRONT);
2079
2080 ParticleTime* life;
2081 ParticleTime lifeParam = 1.0f;
2082 collection->GetStreamOrParameter(PARTICLEUSAGE_LIFE, &life, &lifeParam, PARTICLE_BUFFER_FRONT);
2083
2084 int animationLength = 0;
2085 //int animationDimension = 0;
2086 int animationStride = 0;
2087 bool* animationEnable = NULL;
2088 f32* animationData = NULL;
2089
2090 ResParticleVector2ImmediateUpdater vec2Updater(updater);
2091 NW_ASSERT(vec2Updater.IsValid());
2092
2093 ResParticleAnimation animation = vec2Updater.GetParticleAnimation();
2094 if (animation.IsValid())
2095 {
2096 animationLength = animation.GetAnimationLength();
2097 //animationDimension = animation.GetAnimationDimension();
2098 animationStride = animation.GetAnimationStride();
2099 animationEnable = animation.GetAnimationEnabled();
2100 animationData = animation.GetAnimationData();
2101 }
2102
2103 ParticleUsage target = vec2Updater.GetTargetStream();
2104 nw::math::VEC2* storagePtr = (nw::math::VEC2*)collection->GetStreamPtr(target, PARTICLE_BUFFER_FRONT);
2105 NW_NULL_ASSERT(storagePtr);
2106
2107 const nw::math::VEC2 defaultValue = vec2Updater.GetDefaultValue();
2108
2109 const u32 maxIndex = collection->GetMaxActiveIndex() + 1;
2110 for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index)
2111 {
2112 nw::math::VEC2 value = defaultValue;
2113
2114 bool interp;
2115 f32 interpFactor;
2116 int animationDataIndex;
2117
2118 if (animationData != NULL)
2119 {
2120 ResParticleAnimationOption animationOption = animation.GetParticleAnimationOption();
2121
2122 animationDataIndex = CalcAnimationDataIndex(
2123 animationOption,
2124 index,
2125 birth[index],
2126 (life == NULL) ? lifeParam : life[index],
2127 time,
2128 animationLength,
2129 &interp,
2130 &interpFactor,
2131 updaterIndex);
2132 animationDataIndex *= animationStride;
2133
2134 if (animationEnable[0])
2135 {
2136 value.x = animationData[animationDataIndex++];
2137 }
2138
2139 if (animationEnable[1])
2140 {
2141 value.y = animationData[animationDataIndex++];
2142 }
2143
2144 if (interp)
2145 {
2146 if (animationEnable[0])
2147 {
2148 f32 nextValue = animationData[animationDataIndex++];
2149 value.x = value.x + (nextValue - value.x) * interpFactor;
2150 }
2151
2152 if (animationEnable[1])
2153 {
2154 f32 nextValue = animationData[animationDataIndex++];
2155 value.y = value.y + (nextValue - value.y) * interpFactor;
2156 }
2157 }
2158 }
2159
2160 storagePtr[index] = value;
2161 }
2162
2163 NW_PARTICLE_PROFILE_STOP();
2164 }
2165
ParticleVector3AdditiveUpdater(const ResParticleUpdaterData * updater,ParticleCollection * collection,ParticleTime time,f32 diffTime,int updaterIndex)2166 void ParticleVector3AdditiveUpdater(
2167 const ResParticleUpdaterData* updater,
2168 ParticleCollection* collection,
2169 ParticleTime time,
2170 f32 diffTime,
2171 int updaterIndex
2172 )
2173 {
2174 ParticleTime* birth = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_BIRTH, PARTICLE_BUFFER_FRONT);
2175
2176 ParticleTime* life;
2177 ParticleTime lifeParam = 1.0f;
2178 collection->GetStreamOrParameter(PARTICLEUSAGE_LIFE, &life, &lifeParam, PARTICLE_BUFFER_FRONT);
2179
2180 int animationLength = 0;
2181 //int animationDimension = 0;
2182 int animationStride = 0;
2183 bool* animationEnable = NULL;
2184 f32* animationData = NULL;
2185
2186 ResParticleVector3AdditiveUpdater vec3Updater(updater);
2187 NW_ASSERT(vec3Updater.IsValid());
2188
2189 ParticleUsage target = vec3Updater.GetTargetStream();
2190 nw::math::VEC3* storagePtr = (nw::math::VEC3*)collection->GetStreamPtr(target, PARTICLE_BUFFER_FRONT);
2191 NW_NULL_ASSERT(storagePtr);
2192
2193 const nw::math::VEC3 defaultValue = vec3Updater.GetDefaultValue();
2194
2195 ResParticleAnimation animation = vec3Updater.GetParticleAnimation();
2196 if (animation.IsValid())
2197 {
2198 animationLength = animation.GetAnimationLength();
2199 //animationDimension = animation.GetAnimationDimension();
2200 animationStride = animation.GetAnimationStride();
2201 animationEnable = animation.GetAnimationEnabled();
2202 animationData = animation.GetAnimationData();
2203 }
2204
2205 const u32 maxIndex = collection->GetMaxActiveIndex() + 1;
2206 for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index)
2207 {
2208 // CALC_OPT:コピーをわけることで、適切なタイミングでコピーが実行される模様。
2209 register nw::math::VEC3 value;
2210 value.x = defaultValue.x;
2211 value.y = defaultValue.y;
2212 value.z = defaultValue.z;
2213
2214 bool interp;
2215 f32 interpFactor;
2216 int animationDataIndex;
2217
2218 // CALC_OPT:カーブ無しデータはここまで処理がこない。
2219 // if (animationData != NULL)
2220 {
2221 ResParticleAnimationOption animationOption = animation.GetParticleAnimationOption();
2222
2223 animationDataIndex = CalcAnimationDataIndex(
2224 animationOption,
2225 index,
2226 birth[index],
2227 (life == NULL) ? lifeParam : life[index],
2228 time,
2229 animationLength,
2230 &interp,
2231 &interpFactor,
2232 updaterIndex);
2233 animationDataIndex *= animationStride;
2234
2235 if (animationEnable[0])
2236 {
2237 value.x = animationData[animationDataIndex++];
2238 }
2239
2240 if (animationEnable[1])
2241 {
2242 value.y = animationData[animationDataIndex++];
2243 }
2244
2245 if (animationEnable[2])
2246 {
2247 value.z = animationData[animationDataIndex++];
2248 }
2249
2250 if (interp)
2251 {
2252 if (animationEnable[0])
2253 {
2254 f32 nextValue = animationData[animationDataIndex++];
2255 value.x = value.x + (nextValue - value.x) * interpFactor;
2256 }
2257
2258 if (animationEnable[1])
2259 {
2260 f32 nextValue = animationData[animationDataIndex++];
2261 value.y = value.y + (nextValue - value.y) * interpFactor;
2262 }
2263
2264 if (animationEnable[2])
2265 {
2266 f32 nextValue = animationData[animationDataIndex++];
2267 value.z = value.z + (nextValue - value.z) * interpFactor;
2268 }
2269 }
2270 }
2271
2272 storagePtr[index].x += value.x * diffTime;
2273 storagePtr[index].y += value.y * diffTime;
2274 storagePtr[index].z += value.z * diffTime;
2275 }
2276 }
2277
2278
ParticleVector3ImmediateUpdater(const ResParticleUpdaterData * updater,ParticleCollection * collection,ParticleTime time,int updaterIndex)2279 void ParticleVector3ImmediateUpdater(
2280 const ResParticleUpdaterData* updater,
2281 ParticleCollection* collection,
2282 ParticleTime time,
2283 int updaterIndex
2284 )
2285 {
2286 ParticleTime* birth = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_BIRTH, PARTICLE_BUFFER_FRONT);
2287
2288 ParticleTime* life;
2289 ParticleTime lifeParam = 1.0f;
2290 collection->GetStreamOrParameter(PARTICLEUSAGE_LIFE, &life, &lifeParam, PARTICLE_BUFFER_FRONT);
2291
2292 int animationLength = 0;
2293 //int animationDimension = 0;
2294 int animationStride = 0;
2295 bool* animationEnable = NULL;
2296 f32* animationData = NULL;
2297
2298 ResParticleVector3ImmediateUpdater vec3Updater(updater);
2299 NW_ASSERT(vec3Updater.IsValid());
2300
2301 ResParticleAnimation animation = vec3Updater.GetParticleAnimation();
2302 if (animation.IsValid())
2303 {
2304 animationLength = animation.GetAnimationLength();
2305 //animationDimension = animation.GetAnimationDimension();
2306 animationStride = animation.GetAnimationStride();
2307 animationEnable = animation.GetAnimationEnabled();
2308 animationData = animation.GetAnimationData();
2309 }
2310
2311 ParticleUsage target = vec3Updater.GetTargetStream();
2312 nw::math::VEC3* storagePtr = (nw::math::VEC3*)collection->GetStreamPtr(target, PARTICLE_BUFFER_FRONT);
2313 NW_NULL_ASSERT(storagePtr);
2314
2315 const nw::math::VEC3 defaultValue = vec3Updater.GetDefaultValue();
2316
2317 const u32 maxIndex = collection->GetMaxActiveIndex() + 1;
2318 for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index)
2319 {
2320 nw::math::VEC3 value = defaultValue;
2321
2322 bool interp;
2323 f32 interpFactor;
2324 int animationDataIndex;
2325
2326 // CALC_OPT:カーブ無しデータはここまで処理がこない。
2327 // if (animationData != NULL)
2328 {
2329 ResParticleAnimationOption animationOption = animation.GetParticleAnimationOption();
2330
2331 animationDataIndex = CalcAnimationDataIndex(
2332 animationOption,
2333 index,
2334 birth[index],
2335 (life == NULL) ? lifeParam : life[index],
2336 time,
2337 animationLength,
2338 &interp,
2339 &interpFactor,
2340 updaterIndex);
2341 animationDataIndex *= animationStride;
2342
2343 if (animationEnable[0])
2344 {
2345 value.x = animationData[animationDataIndex++];
2346 }
2347
2348 if (animationEnable[1])
2349 {
2350 value.y = animationData[animationDataIndex++];
2351 }
2352
2353 if (animationEnable[2])
2354 {
2355 value.z = animationData[animationDataIndex++];
2356 }
2357
2358 if (interp)
2359 {
2360 if (animationEnable[0])
2361 {
2362 f32 nextValue = animationData[animationDataIndex++];
2363 value.x = value.x + (nextValue - value.x) * interpFactor;
2364 }
2365
2366 if (animationEnable[1])
2367 {
2368 f32 nextValue = animationData[animationDataIndex++];
2369 value.y = value.y + (nextValue - value.y) * interpFactor;
2370 }
2371
2372 if (animationEnable[2])
2373 {
2374 f32 nextValue = animationData[animationDataIndex++];
2375 value.z = value.z + (nextValue - value.z) * interpFactor;
2376 }
2377 }
2378 }
2379
2380 storagePtr[index] = value;
2381 }
2382 }
2383
ParticleVector3RandomAdditiveUpdater(const ResParticleUpdaterData * updater,ParticleCollection * collection,ParticleTime time,f32 diffTime,int updaterIndex)2384 void ParticleVector3RandomAdditiveUpdater(
2385 const ResParticleUpdaterData* updater,
2386 ParticleCollection* collection,
2387 ParticleTime time,
2388 f32 diffTime,
2389 int updaterIndex
2390 )
2391 {
2392 ParticleTime* birth = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_BIRTH, PARTICLE_BUFFER_FRONT);
2393
2394 ParticleTime* life;
2395 ParticleTime lifeParam = 1.0f;
2396 collection->GetStreamOrParameter(PARTICLEUSAGE_LIFE, &life, &lifeParam, PARTICLE_BUFFER_FRONT);
2397
2398 int animationLength = 0;
2399 //int animationDimension = 0;
2400 int animationStride = 0;
2401 bool* animationEnable = NULL;
2402 f32* animationData = NULL;
2403
2404 ResParticleVector3RandomAdditiveUpdater vec3Updater(updater);
2405 NW_ASSERT(vec3Updater.IsValid());
2406
2407 ResParticleAnimation animation = vec3Updater.GetParticleAnimation();
2408 if (animation.IsValid())
2409 {
2410 animationLength = animation.GetAnimationLength();
2411 //animationDimension = animation.GetAnimationDimension();
2412 animationStride = animation.GetAnimationStride();
2413 animationEnable = animation.GetAnimationEnabled();
2414 animationData = animation.GetAnimationData();
2415 }
2416
2417 ParticleUsage target = vec3Updater.GetTargetStream();
2418 nw::math::VEC3* storagePtr = (nw::math::VEC3*)collection->GetStreamPtr(target, PARTICLE_BUFFER_FRONT);
2419 NW_NULL_ASSERT(storagePtr);
2420
2421 const nw::math::VEC3 defaultValue = vec3Updater.GetDefaultValue();
2422
2423 const u32 maxIndex = collection->GetMaxActiveIndex() + 1;
2424 for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index)
2425 {
2426 nw::math::VEC3 value = defaultValue;
2427
2428 bool interp;
2429 f32 interpFactor;
2430 int animationDataIndex;
2431
2432 // CALC_OPT:カーブ無しデータはここまで処理がこない。
2433 // if (animationData != NULL)
2434 {
2435 ResParticleAnimationOption animationOption = animation.GetParticleAnimationOption();
2436
2437 animationDataIndex = CalcAnimationDataIndex(
2438 animationOption,
2439 index,
2440 birth[index],
2441 (life == NULL) ? lifeParam : life[index],
2442 time,
2443 animationLength,
2444 &interp,
2445 &interpFactor,
2446 updaterIndex);
2447 animationDataIndex *= animationStride;
2448
2449 if (animationEnable[0])
2450 {
2451 value.x = animationData[animationDataIndex++];
2452 }
2453
2454 if (animationEnable[1])
2455 {
2456 value.y = animationData[animationDataIndex++];
2457 }
2458
2459 if (animationEnable[2])
2460 {
2461 value.z = animationData[animationDataIndex++];
2462 }
2463
2464 if (interp)
2465 {
2466 if (animationEnable[0])
2467 {
2468 f32 nextValue = animationData[animationDataIndex++];
2469 value.x = value.x + (nextValue - value.x) * interpFactor;
2470 }
2471
2472 if (animationEnable[1])
2473 {
2474 f32 nextValue = animationData[animationDataIndex++];
2475 value.y = value.y + (nextValue - value.y) * interpFactor;
2476 }
2477
2478 if (animationEnable[2])
2479 {
2480 f32 nextValue = animationData[animationDataIndex++];
2481 value.z = value.z + (nextValue - value.z) * interpFactor;
2482 }
2483 }
2484 }
2485
2486 storagePtr[index] = value * diffTime;
2487 }
2488 }
2489
ParticleRotateUpVectorUpdater(const ResParticleUpdaterData * updater,ParticleCollection * collection)2490 void ParticleRotateUpVectorUpdater(
2491 const ResParticleUpdaterData* updater,
2492 ParticleCollection* collection
2493 )
2494 {
2495 NW_PARTICLE_PROFILE_START("ParticleRotateUpVectorUpdater");
2496
2497 ResParticleRotateUpVectorUpdater vec3Updater(updater);
2498 NW_ASSERT(vec3Updater.IsValid());
2499
2500 NW_ASSERT(vec3Updater.GetTargetStream() == PARTICLEUSAGE_ROTATE);
2501 nw::math::VEC3* storagePtr =
2502 (nw::math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_ROTATE, PARTICLE_BUFFER_FRONT);
2503 NW_NULL_ASSERT(storagePtr);
2504
2505 ResParticleRotateUpVectorUpdater::ParticleRotateUpVectorSource source =
2506 vec3Updater.GetSource();
2507 switch (source)
2508 {
2509 case ResParticleRotateUpVectorUpdater::SOURCE_VELOCITY:
2510 {
2511 nw::math::VEC3* srcStoragePtr =
2512 (nw::math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_VELOCITY, PARTICLE_BUFFER_FRONT);
2513
2514 const u32 maxIndex = collection->GetMaxActiveIndex() + 1;
2515 for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index)
2516 {
2517 nw::math::VEC3 ySrcAxis = srcStoragePtr[index];
2518
2519 if (internal::VEC3Normalize(&ySrcAxis))
2520 {
2521 // CALC_OPT:acosをテーブル引きに。
2522 // 2010/09/27 一旦、精度上の問題で戻しました。
2523 storagePtr[index].x = nw::math::AcosRad(ySrcAxis.y);
2524 // storagePtr[index].x = internal::AcosTableRad(ySrcAxis.y);
2525
2526 // nw::math::Atan2Rad よりも std::atan2fの方が速い(SDK0.13)
2527 storagePtr[index].y = ::std::atan2f(ySrcAxis.x, ySrcAxis.z);
2528
2529 storagePtr[index].z = 0;
2530 }
2531 }
2532 }
2533 break;
2534 case ResParticleRotateUpVectorUpdater::SOURCE_DISTANCE:
2535 {
2536 nw::math::VEC3* srcStoragePtr =
2537 (nw::math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_TRANSLATE, PARTICLE_BUFFER_FRONT);
2538 nw::math::VEC3* optionStoragePtr =
2539 (nw::math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_TRANSLATE, PARTICLE_BUFFER_BACK);
2540
2541 const u32 maxIndex = collection->GetMaxActiveIndex() + 1;
2542 for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index)
2543 {
2544 nw::math::VEC3 ySrcAxis = srcStoragePtr[index];
2545 ySrcAxis -= optionStoragePtr[index];
2546
2547 if (internal::VEC3Normalize(&ySrcAxis))
2548 {
2549 // CALC_OPT:acosをテーブル引きに。
2550 // 2010/09/27 一旦、精度上の問題で戻しました。
2551 storagePtr[index].x = nw::math::AcosRad(ySrcAxis.y);
2552 // storagePtr[index].x = internal::AcosTableRad(ySrcAxis.y);
2553
2554 // nw::math::Atan2Rad よりも std::atan2fの方が速い(SDK0.13)
2555 storagePtr[index].y = ::std::atan2f(ySrcAxis.x, ySrcAxis.z);
2556
2557 storagePtr[index].z = 0;
2558 }
2559 }
2560 }
2561 break;
2562 }
2563
2564 NW_PARTICLE_PROFILE_STOP();
2565 }
2566
ParticleTexturePatternUpdater(const ResParticleUpdaterData * updater,ParticleCollection * collection,ParticleTime time,int updaterIndex)2567 void ParticleTexturePatternUpdater(
2568 const ResParticleUpdaterData* updater,
2569 ParticleCollection* collection,
2570 ParticleTime time,
2571 int updaterIndex
2572 )
2573 {
2574 NW_PARTICLE_PROFILE_START("ParticleTexturePatternUpdater");
2575
2576 ParticleTime* birth = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_BIRTH, PARTICLE_BUFFER_FRONT);
2577
2578 ParticleTime* life;
2579 ParticleTime lifeParam = 1.0f;
2580 collection->GetStreamOrParameter(PARTICLEUSAGE_LIFE, &life, &lifeParam, PARTICLE_BUFFER_FRONT);
2581
2582 int animationLength = 0;
2583 //int animationDimension = 0;
2584 int animationStride = 0;
2585 bool* animationEnable = NULL;
2586 f32* animationData = NULL;
2587
2588 ResParticleTexturePatternUpdater texUpdater(updater);
2589 NW_ASSERT(texUpdater.IsValid());
2590
2591 ResParticleAnimation animation = texUpdater.GetParticleAnimation();
2592 if (animation.IsValid())
2593 {
2594 animationLength = animation.GetAnimationLength();
2595 //animationDimension = animation.GetAnimationDimension();
2596 animationStride = animation.GetAnimationStride();
2597 animationEnable = animation.GetAnimationEnabled();
2598 animationData = animation.GetAnimationData();
2599 }
2600
2601 ParticleUsage target = texUpdater.GetTargetStream();
2602 nw::math::VEC2* storagePtr = (nw::math::VEC2*)collection->GetStreamPtr(target, PARTICLE_BUFFER_FRONT);
2603 NW_NULL_ASSERT(storagePtr);
2604
2605 const int divisionX = (int)texUpdater.GetDivisionX();
2606 const int divisionY = (int)texUpdater.GetDivisionY();
2607
2608 const f32 halfDivisionX = (int)texUpdater.GetDivisionX() * 0.5f;
2609 const f32 halfDivisionY = (int)texUpdater.GetDivisionY() * 0.5f;
2610
2611 const int textureIndexMax = divisionX * divisionY - 1;
2612
2613 const u32 maxIndex = collection->GetMaxActiveIndex() + 1;
2614 for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index)
2615 {
2616 int value = texUpdater.GetTextureIndex();
2617
2618 if (animationData != NULL)
2619 {
2620 ResParticleAnimationOption animationOption = animation.GetParticleAnimationOption();
2621
2622 ParticleTime ptclTime = animationOption.EvaluateAnimationFrame(
2623 index,
2624 birth[index],
2625 (life == NULL) ? lifeParam : life[index],
2626 time,
2627 updaterIndex);
2628
2629 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
2630 if (ptclTime < 0)
2631 {
2632 ptclTime = 0;
2633 }
2634 if (ptclTime >= animationLength)
2635 {
2636 ptclTime = (f32)animationLength - 1;
2637 }
2638
2639 int animationDataPos = (int)ptclTime * animationStride;
2640 #else
2641 if (ptclTime.GetParticleTimeValue() < 0)
2642 {
2643 ptclTime = 0;
2644 }
2645 if (ptclTime >= ParticleTime(animationLength))
2646 {
2647 ptclTime = animationLength - 1;
2648 }
2649
2650 int animationDataPos = ptclTime.GetS32Value() * animationStride;
2651 #endif
2652
2653 if (animationEnable[0])
2654 {
2655 value = (int)animationData[animationDataPos++];
2656 }
2657
2658 if (value < 0)
2659 {
2660 value = 0;
2661 }
2662
2663 if (value > textureIndexMax)
2664 {
2665 value = textureIndexMax;
2666 }
2667 }
2668
2669 storagePtr[index].x = -0.5f + (halfDivisionX) - (value % divisionX);
2670 storagePtr[index].y = 0.5f - (halfDivisionY) + (divisionY - (value / divisionX) - 1);
2671 //storagePtr[index].y = -0.5f + (halfDivisionY) - (divisionY - (value / divisionX) - 1);
2672 }
2673
2674 NW_PARTICLE_PROFILE_STOP();
2675 }
2676
ParticleChildUpdater(const ResParticleUpdaterData * updater,ParticleCollection * collection,u16 * activeIndex,int startIndex,int incrIndex,int count,ParticleTime time,ParticleTime prevTime,u32 work,ParticleSet * parent,ParticleContext * particleContext,ParticleRandom * random)2677 void ParticleChildUpdater(
2678 const ResParticleUpdaterData* updater,
2679 ParticleCollection* collection,
2680 u16* activeIndex,
2681 int startIndex,
2682 int incrIndex,
2683 int count,
2684 ParticleTime time,
2685 ParticleTime prevTime,
2686 u32 work,
2687 ParticleSet* parent,
2688 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
2689 ParticleContext* particleContext,
2690 #endif
2691 ParticleRandom* random
2692 )
2693 {
2694 NW_PARTICLE_PROFILE_START("ParticleChildUpdater");
2695
2696 ParticleTime* birth = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_BIRTH, PARTICLE_BUFFER_FRONT);
2697
2698 ParticleTime* life;
2699 ParticleTime lifeParam = 1.0f;
2700 collection->GetStreamOrParameter(PARTICLEUSAGE_LIFE, &life, &lifeParam, PARTICLE_BUFFER_FRONT);
2701
2702 ResParticleChildUpdater childUpdater(updater);
2703 NW_ASSERT(childUpdater.IsValid());
2704
2705 const ResParticleForm resForm = childUpdater.GetParticleForm();
2706
2707 ParticleSet* particleSet = reinterpret_cast<ParticleSet*>(work);
2708 if (particleSet != NULL && resForm.IsValid())
2709 {
2710 const nw::math::MTX34 modelMtx = parent->WorldMatrix();
2711
2712 nw::math::VEC3* translate =
2713 (nw::math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_TRANSLATE, PARTICLE_BUFFER_FRONT);
2714 #if 0
2715 nw::math::VEC3* rotate;
2716 nw::math::VEC3 rotateParam;
2717 collection->GetStreamOrParameter(PARTICLEUSAGE_ROTATE, &rotate, &rotateParam, PARTICLE_BUFFER_FRONT);
2718
2719 nw::math::VEC3* scale;
2720 nw::math::VEC3 scaleParam;
2721 collection->GetStreamOrParameter(PARTICLEUSAGE_SCALE, &scale, &scaleParam, PARTICLE_BUFFER_FRONT);
2722 #endif
2723
2724 int emissionCount = childUpdater.GetEmissionRatio();
2725 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
2726 int newParticleCount = 0;
2727 nw::math::VEC3* positionsHead = particleContext->GetEmissionPositionWork(emissionCount * count);
2728 u16* parentsHead = particleContext->GetEmissionParentWork(emissionCount * count);
2729 #endif
2730 ResParticleChildUpdaterOption updaterOption = childUpdater.GetTiming();
2731
2732 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
2733 ParticleTime* limit = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_NEG_TIMELIMIT, PARTICLE_BUFFER_FRONT);
2734 #endif
2735
2736 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
2737 #else
2738 // 格納先のポインタ等を集める
2739 ParticleCollection* targetCollection = particleSet->GetParticleCollection();
2740 bool targetIsAscendingOrder = particleSet->IsAscendingOrder();
2741
2742 const int targetIncrIndex = targetIsAscendingOrder ? 1 : -1;
2743
2744 nw::math::VEC3* targetTranslate = (nw::math::VEC3*)targetCollection->GetStreamPtr(PARTICLEUSAGE_TRANSLATE, PARTICLE_BUFFER_FRONT);
2745 NW_NULL_ASSERT(targetTranslate);
2746
2747 bool useTransform = false;
2748 nw::math::VEC3* targetVelocity = (nw::math::VEC3*)targetCollection->GetStreamPtr(PARTICLEUSAGE_VELOCITY, PARTICLE_BUFFER_FRONT);
2749 nw::math::MTX34 transformMatrix;
2750 if (particleSet->WorldMatrix() != modelMtx)
2751 {
2752 useTransform = true;
2753 nw::math::MTX34Mult(&transformMatrix, particleSet->InverseWorldMatrix(), modelMtx);
2754 }
2755
2756 u16* targetActiveIndex = (u16*)targetCollection->GetStreamPtr(PARTICLEUSAGE_ACTIVEINDEX, PARTICLE_BUFFER_FRONT);
2757 NW_NULL_ASSERT(targetActiveIndex);
2758 int targetStartIndex = targetIsAscendingOrder ? targetCollection->GetCount() : targetCollection->GetCapacity() - targetCollection->GetCount() - 1;
2759 targetActiveIndex += targetStartIndex;
2760 #endif
2761
2762 u16* pActiveIndex = activeIndex + startIndex;
2763 for (int j = 0; j < count; ++j)
2764 {
2765 int index = *pActiveIndex;
2766 pActiveIndex += incrIndex;
2767
2768 if (updaterOption.CheckTiming(
2769 birth[index],
2770 (life == NULL) ? lifeParam : life[index],
2771 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
2772 limit[index],
2773 #endif
2774 prevTime,
2775 time))
2776 {
2777 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
2778 nw::math::VEC3* newPositions = &positionsHead[newParticleCount];
2779 if (emissionCount > particleContext->GetEmissionWorkCapacity() - newParticleCount)
2780 {
2781 emissionCount = particleContext->GetEmissionWorkCapacity() - newParticleCount;
2782 }
2783
2784 for (int k = 0; k < emissionCount; ++k)
2785 {
2786 parentsHead[newParticleCount + k] = index;
2787 }
2788 #else
2789 emissionCount = particleSet->AddParticles(emissionCount);
2790 if (emissionCount == 0)
2791 {
2792 break;
2793 }
2794 #endif
2795
2796 const ResParticleCubeForm cubeForm = ResDynamicCast<ResParticleCubeForm>(resForm);
2797 if (cubeForm.IsValid())
2798 {
2799 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
2800 ParticleEmitter::CalcCubeForm(cubeForm, emissionCount, random, newPositions);
2801 #else
2802 ParticleEmitter::CalcCubeForm(cubeForm, emissionCount, random,
2803 targetActiveIndex, targetIncrIndex, targetTranslate);
2804 #endif
2805 }
2806
2807 const ResParticleCylinderForm cylinderForm = ResDynamicCast<ResParticleCylinderForm>(resForm);
2808 if (cylinderForm.IsValid())
2809 {
2810 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
2811 ParticleEmitter::CalcCylinderForm(cylinderForm, emissionCount, random, newPositions);
2812 #else
2813 ParticleEmitter::CalcCylinderForm(cylinderForm, emissionCount, random,
2814 targetActiveIndex, targetIncrIndex, targetTranslate);
2815 #endif
2816 }
2817
2818 const ResParticleDiscForm discForm = ResDynamicCast<ResParticleDiscForm>(resForm);
2819 if (discForm.IsValid())
2820 {
2821 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
2822 ParticleEmitter::CalcDiscForm(discForm, emissionCount, random, newPositions);
2823 #else
2824 ParticleEmitter::CalcDiscForm(discForm, emissionCount, random,
2825 targetActiveIndex, targetIncrIndex, targetTranslate);
2826 #endif
2827 }
2828
2829 const ResParticlePointForm pointForm = ResDynamicCast<ResParticlePointForm>(resForm);
2830 if (pointForm.IsValid())
2831 {
2832 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
2833 ParticleEmitter::CalcPointForm(pointForm, emissionCount, random, newPositions);
2834 #else
2835 ParticleEmitter::CalcPointForm(pointForm, emissionCount, random,
2836 targetActiveIndex, targetIncrIndex, targetTranslate);
2837 #endif
2838 }
2839
2840 const ResParticleSphereForm sphereForm = ResDynamicCast<ResParticleSphereForm>(resForm);
2841 if (sphereForm.IsValid())
2842 {
2843 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
2844 ParticleEmitter::CalcSphereForm(sphereForm, emissionCount, random, newPositions);
2845 #else
2846 ParticleEmitter::CalcSphereForm(sphereForm, emissionCount, random,
2847 targetActiveIndex, targetIncrIndex, targetTranslate);
2848 #endif
2849 }
2850
2851 const ResParticleRectangleForm rectangleForm = ResDynamicCast<ResParticleRectangleForm>(resForm);
2852 if (rectangleForm.IsValid())
2853 {
2854 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
2855 ParticleEmitter::CalcRectangleForm(rectangleForm, emissionCount, random, newPositions);
2856 #else
2857 ParticleEmitter::CalcRectangleForm(rectangleForm, emissionCount, random,
2858 targetActiveIndex, targetIncrIndex, targetTranslate);
2859 #endif
2860 }
2861
2862 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
2863 newParticleCount += emissionCount;
2864 #else
2865 {
2866 ParticleModel* model = static_cast<ParticleModel*>(particleSet->GetParent());
2867 NW_NULL_ASSERT(model);
2868 ParticleTime targetTime = model->ParticleAnimFrameController().GetFrame();
2869
2870 particleSet->InitializeParticles(targetStartIndex, emissionCount, targetIncrIndex, targetTime);
2871 targetStartIndex += targetIncrIndex * emissionCount;
2872 }
2873
2874 // 親の速度
2875 bool isInheritParentVelocity = particleSet->GetResParticleSet().GetIsInheritParentVelocity();
2876
2877 math::VEC3* parentVelocity = NULL;
2878 if (isInheritParentVelocity)
2879 {
2880 parentVelocity = (math::VEC3*)parent->GetParticleCollection()->GetStreamPtr(
2881 PARTICLEUSAGE_VELOCITY, PARTICLE_BUFFER_FRONT);
2882 }
2883
2884 if (useTransform)
2885 {
2886 nw::math::MTX34 workMatrix;
2887
2888 nw::math::MTX34 particleMatrix;
2889 nw::math::MTX34Identity(&particleMatrix);
2890
2891 #if 0
2892 nw::math::MTX34Translate(&particleMatrix, translate[index]);
2893 ////nw::math::MTX34RotXYZRad(&workMatrix, rotate[index].x, rotate[index].y, rotate[index].z);
2894 ////nw::math::MTX34Mult(&particleMatrix, particleMatrix, workMatrix);
2895 ////nw::math::MTX34Scale(&workMatrix, scale[index]);
2896 ////nw::math::MTX34Mult(&particleMatrix, particleMatrix, workMatrix);
2897
2898 nw::math::MTX34Mult(&workMatrix, transformMatrix, particleMatrix);
2899
2900 for (int i = 0; i < emissionCount; ++i)
2901 {
2902 int dstIndex = *targetActiveIndex;
2903 targetActiveIndex += targetIncrIndex;
2904
2905 nw::math::VEC3Transform(&targetTranslate[dstIndex], workMatrix, targetTranslate[dstIndex]);
2906 nw::math::VEC3TransformNormal(&targetVelocity[dstIndex], workMatrix, targetVelocity[dstIndex]);
2907 }
2908 #else
2909 for (int i = 0; i < emissionCount; ++i)
2910 {
2911 int dstIndex = *targetActiveIndex;
2912 targetActiveIndex += targetIncrIndex;
2913
2914 targetTranslate[dstIndex] += translate[index];
2915
2916 nw::math::VEC3Transform(&targetTranslate[dstIndex], transformMatrix, targetTranslate[dstIndex]);
2917
2918 if (isInheritParentVelocity)
2919 {
2920 targetVelocity[dstIndex] += parentVelocity[index];
2921 }
2922
2923 nw::math::VEC3TransformNormal(&targetVelocity[dstIndex], transformMatrix, targetVelocity[dstIndex]);
2924 }
2925 #endif
2926 }
2927 else
2928 {
2929 for (int i = 0; i < emissionCount; ++i)
2930 {
2931 int dstIndex = *targetActiveIndex;
2932 targetActiveIndex += targetIncrIndex;
2933
2934 targetTranslate[dstIndex] += translate[index];
2935
2936 if (isInheritParentVelocity)
2937 {
2938 targetVelocity[dstIndex] += parentVelocity[index];
2939 }
2940 }
2941 }
2942 #endif
2943 }
2944 }
2945
2946 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
2947 if (newParticleCount > 0)
2948 {
2949 particleSet->AddParticles(
2950 modelMtx,
2951 positionsHead,
2952 parent,
2953 parentsHead,
2954 newParticleCount);
2955 }
2956 #endif
2957 }
2958
2959 NW_PARTICLE_PROFILE_STOP();
2960 }
2961
2962 //----------------------------------------
2963 void
UpdateParticles(ParticleContext * particleContext)2964 ParticleSet::UpdateParticles(ParticleContext* particleContext)
2965 {
2966 NW_ASSERT(ut::IsTypeOf<ParticleModel>(this->GetParent()));
2967 ParticleModel* model = static_cast<ParticleModel*>(this->GetParent());
2968 NW_NULL_ASSERT(model);
2969
2970 ParticleCollection* collection = this->GetParticleCollection();
2971 collection->SwapBuffer();
2972
2973 const ParticleTime prevTime = model->ParticleAnimFrameController().GetAnimFrame().GetLastFrame();
2974 const ParticleTime time = model->ParticleAnimFrameController().GetFrame();
2975 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
2976 const f32 diffTime = time - prevTime;
2977 if (diffTime < 0)
2978 {
2979 return;
2980 }
2981 #else
2982 const ParticleTime diffTimePtcl = time - prevTime;
2983 if (diffTimePtcl < 0)
2984 {
2985 return;
2986 }
2987 const f32 diffTime = diffTimePtcl.GetFloat32Value();
2988 #endif
2989
2990 const int collectionCapacity = collection->GetCapacity();
2991 const int count = collection->GetCount();
2992 if (count == 0)
2993 {
2994 return;
2995 }
2996
2997 if (!m_Updaters.empty())
2998 {
2999 int updaterIndex = 0;
3000 ut::MoveArray<Updater>::iterator endIter = this->m_Updaters.end();
3001 for (ut::MoveArray<Updater>::iterator iter = this->m_Updaters.begin(); iter != endIter; ++updaterIndex)
3002 {
3003 Updater& workUpdater = *iter++;
3004 if (workUpdater.resource == NULL)
3005 {
3006 continue;
3007 }
3008
3009 const ResParticleUpdater updater(workUpdater.resource);
3010 if (!updater.GetUpdaterEnabled())
3011 {
3012 continue;
3013 }
3014
3015 void* targetStream;
3016 if (collection->GetBufferSide())
3017 {
3018 targetStream = workUpdater.m_TargetStreams[1];
3019 }
3020 else
3021 {
3022 targetStream = workUpdater.m_TargetStreams[0];
3023 }
3024
3025 switch (workUpdater.m_Type)
3026 {
3027 case ResParticleAccelarationUpdater::TYPE_INFO:
3028 ParticleAccelarationUpdater(updater.ptr(), collection, diffTime);
3029 break;
3030 case ResParticleFloatImmediateUpdater::TYPE_INFO:
3031 ParticleFloatImmediateUpdater(updater.ptr(), collection, time, updaterIndex);
3032 break;
3033 case ResParticleFloatImmediate4KeyUpdater::TYPE_INFO:
3034 ParticleFloatImmediate4KeyUpdater(updater.ptr(), collection, time);
3035 break;
3036 case ResParticleVector3Immediate4KeyUpdater::TYPE_INFO:
3037 ParticleVector3Immediate4KeyUpdater(updater.ptr(), collection, time);
3038 break;
3039 case ResParticleGravityUpdater::TYPE_INFO:
3040 ParticleGravityUpdater((nw::math::VEC3*)targetStream, updater.ptr(), collection, diffTime);
3041 break;
3042 case ResParticleSpinUpdater::TYPE_INFO:
3043 ParticleSpinUpdater(updater.ptr(), collection, diffTime);
3044 break;
3045 case ResParticleRandomUpdater::TYPE_INFO:
3046 ParticleRandomUpdater(updater.ptr(), collection, time, prevTime, this->m_ParticleRandom);
3047 break;
3048 case ResParticleVector2ImmediateUpdater::TYPE_INFO:
3049 ParticleVector2ImmediateUpdater(updater.ptr(), collection, time, updaterIndex);
3050 break;
3051 case ResParticleVector3AdditiveUpdater::TYPE_INFO:
3052 {
3053 NW_PARTICLE_PROFILE_START("ParticleVector3AdditiveUpdater");
3054
3055 if (workUpdater.IsEnabledFlags(Updater::FLAG_IS_HAS_CURVE_ANIM))
3056 {
3057 ParticleVector3AdditiveUpdater(updater.ptr(), collection, time, diffTime, updaterIndex);
3058 }
3059 else
3060 {
3061 ResParticleVector3AdditiveUpdater vec3Updater(updater.ptr());
3062 NW_ASSERT(vec3Updater.IsValid());
3063
3064 nw::math::VEC3* storagePtr = (nw::math::VEC3*)targetStream;
3065 NW_NULL_ASSERT(storagePtr);
3066
3067 nw::math::VEC3 defaultValue = vec3Updater.GetDefaultValue();
3068 defaultValue *= diffTime;
3069
3070 storagePtr += collection->GetMinActiveIndex();
3071 const int processCount = collection->GetMaxActiveIndex() - collection->GetMinActiveIndex() + 1;
3072 internal::VEC3ArrayAddScalar(storagePtr, &defaultValue, processCount);
3073 }
3074
3075 NW_PARTICLE_PROFILE_STOP();
3076 }
3077 break;
3078 case ResParticleVector3ImmediateUpdater::TYPE_INFO:
3079 {
3080 NW_PARTICLE_PROFILE_START("ParticleVector3ImmediateUpdater");
3081
3082 if (workUpdater.IsEnabledFlags(Updater::FLAG_IS_HAS_CURVE_ANIM))
3083 {
3084 ParticleVector3ImmediateUpdater(updater.ptr(), collection, time, updaterIndex);
3085 }
3086 else
3087 {
3088 ResParticleVector3ImmediateUpdater vec3Updater(updater.ptr());
3089 NW_ASSERT(vec3Updater.IsValid());
3090 const nw::math::VEC3 defaultValue = vec3Updater.GetDefaultValue();
3091
3092 nw::math::VEC3* storagePtr = (nw::math::VEC3*)targetStream;
3093 NW_NULL_ASSERT(storagePtr);
3094
3095 const u32 maxIndex = collection->GetMaxActiveIndex() + 1;
3096 for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index)
3097 {
3098 storagePtr[index] = defaultValue;
3099 }
3100 }
3101
3102 NW_PARTICLE_PROFILE_STOP();
3103 }
3104 break;
3105 case ResParticleVector3RandomAdditiveUpdater::TYPE_INFO:
3106 {
3107 NW_PARTICLE_PROFILE_START("ParticleVector3RandomAdditiveUpdater");
3108
3109 if (workUpdater.IsEnabledFlags(Updater::FLAG_IS_HAS_CURVE_ANIM))
3110 {
3111 ParticleVector3RandomAdditiveUpdater(updater.ptr(), collection, time, diffTime, updaterIndex);
3112 }
3113 else
3114 {
3115 ResParticleVector3RandomAdditiveUpdater vec3Updater(updater.ptr());
3116 NW_ASSERT(vec3Updater.IsValid());
3117 const nw::math::VEC3 defaultValue = vec3Updater.GetDefaultValue();
3118
3119 nw::math::VEC3* storagePtr = (nw::math::VEC3*)targetStream;
3120 NW_NULL_ASSERT(storagePtr);
3121
3122 const u32 maxIndex = collection->GetMaxActiveIndex() + 1;
3123 for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index)
3124 {
3125 storagePtr[index] = defaultValue * diffTime;
3126 }
3127 }
3128
3129 NW_PARTICLE_PROFILE_STOP();
3130 }
3131 break;
3132 case ResParticleRotateUpVectorUpdater::TYPE_INFO:
3133 ParticleRotateUpVectorUpdater(updater.ptr(), collection);
3134 break;
3135 case ResParticleTexturePatternUpdater::TYPE_INFO:
3136 ParticleTexturePatternUpdater(updater.ptr(), collection, time, updaterIndex);
3137 break;
3138 case ResParticleChildUpdater::TYPE_INFO:
3139 {
3140 u16* activeIndex = (u16*)collection->GetStreamPtr(PARTICLEUSAGE_ACTIVEINDEX, PARTICLE_BUFFER_BACK);
3141 const bool isAscendingOrder = this->IsAscendingOrder();
3142 const int startIndex = (isAscendingOrder) ? 0 : collectionCapacity - 1;
3143 const int incrIndex = (isAscendingOrder) ? 1 : -1;
3144
3145 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
3146 ParticleChildUpdater(updater.ptr(), collection, activeIndex, startIndex, incrIndex, count, time, prevTime, workUpdater.work, this, particleContext, &this->m_ParticleRandom);
3147 #else
3148 ParticleChildUpdater(updater.ptr(), collection, activeIndex, startIndex, incrIndex, count, time, prevTime, workUpdater.work, this, &this->m_ParticleRandom);
3149 #endif
3150 }
3151 break;
3152 case ResParticleUserUpdater::TYPE_INFO:
3153 {
3154 NW_PARTICLE_PROFILE_START("ParticleUserUpdater");
3155
3156 ResParticleUserUpdater userUpdater(updater.ptr());
3157 NW_ASSERT(userUpdater.IsValid());
3158
3159 if (workUpdater.work != 0)
3160 {
3161 (*(ResParticleUserUpdater::UserFunctionType)workUpdater.work)(
3162 particleContext,
3163 this, // ParticleSet*
3164 &userUpdater,
3165 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
3166 prevTime,
3167 time
3168 #else
3169 prevTime.GetFloat32Value(),
3170 time.GetFloat32Value()
3171 #endif
3172 );
3173 }
3174
3175 NW_PARTICLE_PROFILE_STOP();
3176 }
3177 break;
3178 case ResParticleGeneralUpdater::TYPE_INFO:
3179 // 位置の更新
3180 {
3181 NW_PARTICLE_PROFILE_START("ParticleGeneralUpdater");
3182
3183 math::VEC3* trans =
3184 (math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_TRANSLATE, PARTICLE_BUFFER_FRONT);
3185
3186 math::VEC3* velocity =
3187 (math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_VELOCITY, PARTICLE_BUFFER_FRONT);
3188
3189 trans += collection->GetMinActiveIndex();
3190 velocity += collection->GetMinActiveIndex();
3191 const int processCount = collection->GetMaxActiveIndex() - collection->GetMinActiveIndex() + 1;
3192 internal::VEC3ArrayAddVEC3Array(trans, velocity, diffTime, processCount);
3193
3194 NW_PARTICLE_PROFILE_STOP();
3195 }
3196 break;
3197 }
3198 }
3199 }
3200
3201 // 消滅処理
3202 {
3203 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
3204 int minActiveIndex = collection->GetMinActiveIndex();
3205 int maxActiveIndex = collection->GetMaxActiveIndex();
3206 u32* work = reinterpret_cast<u32*>(particleContext->GetEmissionPositionWork(
3207 (minActiveIndex + ut::RoundUp(maxActiveIndex - minActiveIndex + 1, 8) + 2) / 3)); // TBD
3208
3209 if (minActiveIndex <= maxActiveIndex)
3210 {
3211 NW_ASSERT(particleContext->GetEmissionWorkCapacity() * 3 >=
3212 minActiveIndex + ut::RoundUp(maxActiveIndex - minActiveIndex + 1, 8));
3213 f32* limit = (f32*)collection->GetStreamPtr(PARTICLEUSAGE_NEG_TIMELIMIT, PARTICLE_BUFFER_FRONT);
3214
3215 internal::CalcRemainFrame(
3216 reinterpret_cast<f32*>(&work[minActiveIndex]),
3217 &limit[minActiveIndex],
3218 time,
3219 maxActiveIndex - minActiveIndex + 1);
3220 }
3221
3222 int aliveCount = 0;
3223 int destroyCount = 0;
3224 const int prevCollectionCount = collection->GetCount();
3225
3226 const bool isAscendingOrder = this->IsAscendingOrder();
3227 const int startIndex = (isAscendingOrder) ? 0 : collectionCapacity - 1;
3228 const int incrIndex = (isAscendingOrder) ? 1 : -1;
3229
3230 u16* activeIndex = (u16*)collection->GetStreamPtr(PARTICLEUSAGE_ACTIVEINDEX, PARTICLE_BUFFER_BACK);
3231 u16* newActiveIndex = (u16*)collection->GetStreamPtr(PARTICLEUSAGE_ACTIVEINDEX, PARTICLE_BUFFER_FRONT);
3232 u16* freeIndex = (u16*)collection->GetStreamPtr(PARTICLEUSAGE_FREEINDEX, PARTICLE_BUFFER_FRONT);
3233
3234 minActiveIndex = 0xffff;
3235 maxActiveIndex = 0;
3236 {
3237 u16* pActiveIndex = activeIndex + startIndex;
3238 u16* pAliveIndex = newActiveIndex + startIndex;
3239 u16* pDestroyIndex = freeIndex + collectionCapacity - prevCollectionCount;
3240
3241 for (int i = prevCollectionCount; i != 0; --i)
3242 {
3243 int index = *pActiveIndex;
3244 pActiveIndex += incrIndex;
3245 NW_ASSERT(index != 0xffff);
3246
3247 if ((work[index] & 0x80000000) == 0)
3248 {
3249 *pDestroyIndex++ = index;
3250 ++destroyCount;
3251 }
3252 else
3253 {
3254 if (minActiveIndex > index)
3255 {
3256 minActiveIndex = index;
3257 }
3258
3259 if (maxActiveIndex < index)
3260 {
3261 maxActiveIndex = index;
3262 }
3263
3264 *pAliveIndex = index; // 別バッファにコピーしつつ整列していく
3265 pAliveIndex += incrIndex;
3266 ++aliveCount;
3267 }
3268 }
3269 }
3270 #else
3271 ParticleTime* birth = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_BIRTH, PARTICLE_BUFFER_FRONT);
3272
3273 int aliveCount = 0;
3274 const int prevCollectionCount = collection->GetCount();
3275
3276 const bool isAscendingOrder = this->IsAscendingOrder();
3277 const int startIndex = (isAscendingOrder) ? 0 : collectionCapacity - 1;
3278 const int incrIndex = (isAscendingOrder) ? 1 : -1;
3279
3280 u16* pActiveIndex = (u16*)collection->GetStreamPtr(PARTICLEUSAGE_ACTIVEINDEX, PARTICLE_BUFFER_BACK);
3281 pActiveIndex += startIndex;
3282
3283 u16* pAliveIndex = (u16*)collection->GetStreamPtr(PARTICLEUSAGE_ACTIVEINDEX, PARTICLE_BUFFER_FRONT);
3284 pAliveIndex += startIndex;
3285
3286 u16* pDestroyIndex = (u16*)collection->GetStreamPtr(PARTICLEUSAGE_FREEINDEX, PARTICLE_BUFFER_FRONT);
3287 pDestroyIndex += collectionCapacity - prevCollectionCount;
3288
3289 int minActiveIndex = 0xffff;
3290 int maxActiveIndex = 0;
3291
3292 if (collection->IsStream(PARTICLEUSAGE_LIFE))
3293 {
3294 ParticleTime* life =
3295 (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_LIFE, PARTICLE_BUFFER_FRONT);
3296
3297 for (int i = prevCollectionCount; i != 0; --i)
3298 {
3299 int index = *pActiveIndex;
3300 pActiveIndex += incrIndex;
3301 NW_ASSERT(index != 0xffff);
3302
3303 if (birth[index].GetParticleTimeValue() + life[index].GetParticleTimeValue() <= time.GetParticleTimeValue())
3304 {
3305 *pDestroyIndex++ = index;
3306 }
3307 else
3308 {
3309 if (minActiveIndex > index)
3310 {
3311 minActiveIndex = index;
3312 }
3313
3314 if (maxActiveIndex < index)
3315 {
3316 maxActiveIndex = index;
3317 }
3318
3319 *pAliveIndex = index; // 別バッファにコピーしつつ整列していく
3320 pAliveIndex += incrIndex;
3321 ++aliveCount;
3322 }
3323 }
3324 }
3325 else
3326 {
3327 ParticleTime* lifeParam =
3328 (ParticleTime*)collection->GetParameterPtr(PARTICLEUSAGE_LIFE);
3329
3330 ParticleTime limit = time - *lifeParam;
3331
3332 for (int i = prevCollectionCount; i != 0; --i)
3333 {
3334 int index = *pActiveIndex;
3335 pActiveIndex += incrIndex;
3336 NW_ASSERT(index != 0xffff);
3337
3338 if (birth[index].GetParticleTimeValue() <= limit.GetParticleTimeValue())
3339 {
3340 *pDestroyIndex++ = index;
3341 }
3342 else
3343 {
3344 if (minActiveIndex > index)
3345 {
3346 minActiveIndex = index;
3347 }
3348
3349 if (maxActiveIndex < index)
3350 {
3351 maxActiveIndex = index;
3352 }
3353
3354 *pAliveIndex = index; // 別バッファにコピーしつつ整列していく
3355 pAliveIndex += incrIndex;
3356 ++aliveCount;
3357 }
3358 }
3359 }
3360 #endif
3361
3362 collection->SetMinActiveIndex(minActiveIndex);
3363 collection->SetMaxActiveIndex(maxActiveIndex);
3364 collection->SetCount(aliveCount);
3365 }
3366 }
3367
3368 //----------------------------------------
ParticleSet(os::IAllocator * allocator,ResParticleSet resObj,const ParticleSet::Description & description)3369 ParticleSet::ParticleSet(
3370 os::IAllocator* allocator,
3371 ResParticleSet resObj,
3372 const ParticleSet::Description& description)
3373 : SceneNode(allocator, resObj, description),
3374 m_ParticleCollection(NULL),
3375 m_ScaleOffset(1.0f, 1.0f, 1.0f),
3376 m_RotateOffset(0, 0, 0)
3377 {
3378 }
3379
3380 //----------------------------------------
~ParticleSet()3381 ParticleSet::~ParticleSet()
3382 {
3383 {
3384 ut::MoveArray<Initializer>::iterator endIter = this->m_Initializers.end();
3385 for (ut::MoveArray<Initializer>::iterator iter = this->m_Initializers.begin(); iter != endIter; ++iter)
3386 {
3387 if ((*iter).m_IsCopied)
3388 {
3389 ResParticleInitializerData* ptr = const_cast<ResParticleInitializerData*>((*iter).resource);
3390 this->GetAllocator().Free(ptr);
3391 (*iter).resource = NULL;
3392 }
3393 }
3394 }
3395
3396 {
3397 ut::MoveArray<Updater>::iterator endIter = this->m_Updaters.end();
3398 for (ut::MoveArray<Updater>::iterator iter = this->m_Updaters.begin(); iter != endIter; ++iter)
3399 {
3400 if ((*iter).m_IsCopied)
3401 {
3402 ResParticleUpdaterData* ptr = const_cast<ResParticleUpdaterData*>((*iter).resource);
3403 this->GetAllocator().Free(ptr);
3404 (*iter).resource = NULL;
3405 }
3406 }
3407 }
3408
3409 SafeDestroy(m_ParticleCollection);
3410 }
3411
3412 //----------------------------------------
3413 void
Accept(ISceneVisitor * visitor)3414 ParticleSet::Accept(
3415 ISceneVisitor* visitor
3416 )
3417 {
3418 visitor->VisitParticleSet(this);
3419 AcceptChildren(visitor);
3420 }
3421
3422 //----------------------------------------
3423 void
ClearParticleCollection()3424 ParticleSet::ClearParticleCollection()
3425 {
3426 if (this->m_ParticleCollection == NULL)
3427 {
3428 return;
3429 }
3430
3431 this->m_ParticleCollection->Clear();
3432 }
3433
3434 //----------------------------------------
3435 bool
CheckTiming(ParticleTime birth,ParticleTime life,ParticleTime limit,ParticleTime prevTime,ParticleTime currentTime)3436 ResParticleChildUpdaterOption::CheckTiming(
3437 ParticleTime birth,
3438 ParticleTime life,
3439 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
3440 ParticleTime limit,
3441 #endif
3442 ParticleTime prevTime,
3443 ParticleTime currentTime)
3444 {
3445 switch (this->GetTypeInfo())
3446 {
3447 case ResParticleChildUpdaterFirstUpdateOption::TYPE_INFO:
3448 return birth == currentTime;
3449
3450 case ResParticleChildUpdaterFinalUpdateOption::TYPE_INFO:
3451 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
3452 return limit + currentTime >= 0;
3453 #else
3454 return birth.GetParticleTimeValue() + life.GetParticleTimeValue() <= currentTime.GetParticleTimeValue();
3455 #endif
3456
3457 case ResParticleChildUpdaterIntervalOption::TYPE_INFO:
3458 {
3459 ResParticleChildUpdaterIntervalOption resource(this->ptr());
3460
3461 if (prevTime >= currentTime)
3462 {
3463 return false;
3464 }
3465
3466 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
3467 int end = (int)(resource.GetEnd() * life);
3468
3469 int prevCount =(int)(prevTime - birth);
3470 #else
3471 int end = (int)(resource.GetEnd() * life.GetFloat32Value());
3472
3473 ParticleTime prev_birth = prevTime - birth;
3474 int prevCount = prev_birth.GetIntegralParts();
3475 #endif
3476 if (prevCount >= end)
3477 {
3478 return false;
3479 }
3480
3481 int interval = resource.GetInterval();
3482 if (interval < 1)
3483 {
3484 interval = 1;
3485 }
3486
3487 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
3488 int start = (int)(resource.GetStart() * life);
3489 prevCount = (int)math::FFloor((float)(prevCount - start) / interval);
3490
3491 int currentCount = (int)math::FFloor((currentTime - birth - start) / interval);
3492 #else
3493 int start = (int)(resource.GetStart() * life.GetFloat32Value());
3494 prevCount = (int)math::FFloor((float)(prevCount - start) / interval);
3495
3496 ParticleTime current_birth =currentTime - birth;
3497 current_birth -= start;
3498 int currentCount = (int)math::FFloor(current_birth.GetFloat32Value() / interval);
3499 #endif
3500 if (currentCount < 0)
3501 {
3502 return false;
3503 }
3504
3505 return prevCount < currentCount;
3506 }
3507
3508 case ResParticleChildUpdaterFrameOption::TYPE_INFO:
3509 {
3510 ResParticleChildUpdaterFrameOption resource(this->ptr());
3511
3512 if (prevTime >= currentTime)
3513 {
3514 return false;
3515 }
3516
3517 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
3518 int prevCount =(int)(prevTime - birth);
3519 #else
3520 ParticleTime prev_birth = prevTime - birth;
3521 int prevCount = prev_birth.GetIntegralParts();
3522 #endif
3523 if (prevCount >= resource.GetEnd())
3524 {
3525 return false;
3526 }
3527
3528 int interval = resource.GetInterval();
3529 if (interval < 1)
3530 {
3531 interval = 1;
3532 }
3533
3534 prevCount = (int)math::FFloor((float)(prevCount - resource.GetStart()) / interval);
3535
3536 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
3537 int currentCount = (int)math::FFloor((currentTime - birth - resource.GetStart()) / interval);
3538 #else
3539 ParticleTime current_birth =currentTime - birth;
3540 current_birth -= resource.GetStart();
3541 int currentCount = (int)math::FFloor(current_birth.GetFloat32Value() / interval);
3542 #endif
3543 if (currentCount < 0)
3544 {
3545 return false;
3546 }
3547
3548 return prevCount < currentCount;
3549 }
3550 }
3551
3552 return false;
3553 }
3554
3555 } // namespace gfx
3556 } // namespace nw
3557