/*---------------------------------------------------------------------------* Project: NintendoWare File: gfx_ParticleSet.cpp Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Revision: 26258 $ *---------------------------------------------------------------------------*/ #include "precompiled.h" #include #include #include #include #include #include #include #include #include namespace nw { namespace gfx { namespace internal { #if 0 static const f32 acosTable[] = { 3.141593f,3.067040f,3.036135f,3.012403f,2.992383f,2.974733f,2.958764f,2.944069f,2.930382f,2.917517f,2.905342f,2.893752f, 2.882671f,2.872035f,2.861794f,2.851906f,2.842335f,2.833052f,2.824032f,2.815253f,2.806697f,2.798345f,2.790184f,2.782202f, 2.774385f,2.766724f,2.759209f,2.751832f,2.744585f,2.737462f,2.730455f,2.723559f,2.716768f,2.710078f,2.703484f,2.696981f, 2.690566f,2.684235f,2.677984f,2.671810f,2.665710f,2.659682f,2.653723f,2.647830f,2.642000f,2.636232f,2.630524f,2.624873f, 2.619278f,2.613737f,2.608248f,2.602809f,2.597420f,2.592077f,2.586782f,2.581531f,2.576324f,2.571159f,2.566035f,2.560952f, 2.555907f,2.550901f,2.545932f,2.540999f,2.536101f,2.531238f,2.526408f,2.521611f,2.516846f,2.512112f,2.507409f,2.502736f, 2.498092f,2.493476f,2.488889f,2.484329f,2.479795f,2.475288f,2.470807f,2.466351f,2.461919f,2.457511f,2.453128f,2.448767f, 2.444430f,2.440114f,2.435821f,2.431549f,2.427298f,2.423068f,2.418859f,2.414669f,2.410499f,2.406348f,2.402216f,2.398103f, 2.394008f,2.389932f,2.385872f,2.381831f,2.377806f,2.373798f,2.369807f,2.365833f,2.361874f,2.357931f,2.354003f,2.350091f, 2.346194f,2.342312f,2.338444f,2.334590f,2.330751f,2.326926f,2.323115f,2.319317f,2.315532f,2.311761f,2.308003f,2.304257f, 2.300524f,2.296803f,2.293095f,2.289399f,2.285714f,2.282042f,2.278381f,2.274731f,2.271093f,2.267466f,2.263849f,2.260244f, 2.256649f,2.253065f,2.249491f,2.245928f,2.242375f,2.238831f,2.235298f,2.231774f,2.228260f,2.224755f,2.221260f,2.217774f, 2.214298f,2.210830f,2.207371f,2.203921f,2.200480f,2.197047f,2.193623f,2.190207f,2.186800f,2.183401f,2.180009f,2.176626f, 2.173251f,2.169884f,2.166524f,2.163172f,2.159827f,2.156490f,2.153161f,2.149838f,2.146523f,2.143215f,2.139914f,2.136620f, 2.133333f,2.130052f,2.126779f,2.123511f,2.120251f,2.116997f,2.113750f,2.110508f,2.107273f,2.104045f,2.100822f,2.097606f, 2.094395f,2.091191f,2.087992f,2.084799f,2.081612f,2.078431f,2.075255f,2.072084f,2.068920f,2.065760f,2.062606f,2.059458f, 2.056314f,2.053176f,2.050043f,2.046916f,2.043792f,2.040675f,2.037562f,2.034454f,2.031350f,2.028252f,2.025158f,2.022069f, 2.018985f,2.015904f,2.012829f,2.009758f,2.006692f,2.003630f,2.000572f,1.997518f,1.994469f,1.991424f,1.988383f,1.985346f, 1.982313f,1.979284f,1.976260f,1.973239f,1.970222f,1.967208f,1.964199f,1.961193f,1.958191f,1.955193f,1.952199f,1.949207f, 1.946220f,1.943236f,1.940256f,1.937278f,1.934305f,1.931334f,1.928367f,1.925404f,1.922443f,1.919486f,1.916532f,1.913581f, 1.910633f,1.907688f,1.904747f,1.901808f,1.898872f,1.895939f,1.893010f,1.890083f,1.887158f,1.884237f,1.881318f,1.878402f, 1.875489f,1.872578f,1.869671f,1.866765f,1.863862f,1.860962f,1.858064f,1.855169f,1.852276f,1.849386f,1.846498f,1.843612f, 1.840729f,1.837848f,1.834969f,1.832093f,1.829219f,1.826347f,1.823477f,1.820609f,1.817743f,1.814879f,1.812018f,1.809158f, 1.806301f,1.803445f,1.800591f,1.797739f,1.794889f,1.792041f,1.789195f,1.786351f,1.783508f,1.780667f,1.777828f,1.774990f, 1.772154f,1.769320f,1.766487f,1.763656f,1.760827f,1.757999f,1.755172f,1.752348f,1.749524f,1.746702f,1.743881f,1.741062f, 1.738244f,1.735428f,1.732613f,1.729799f,1.726986f,1.724175f,1.721365f,1.718556f,1.715748f,1.712941f,1.710136f,1.707331f, 1.704528f,1.701726f,1.698924f,1.696124f,1.693325f,1.690527f,1.687729f,1.684933f,1.682137f,1.679343f,1.676549f,1.673756f, 1.670964f,1.668172f,1.665382f,1.662592f,1.659803f,1.657014f,1.654226f,1.651439f,1.648653f,1.645867f,1.643081f,1.640297f, 1.637512f,1.634729f,1.631945f,1.629163f,1.626380f,1.623599f,1.620817f,1.618036f,1.615255f,1.612475f,1.609695f,1.606915f, 1.604136f,1.601357f,1.598578f,1.595799f,1.593020f,1.590242f,1.587464f,1.584686f,1.581908f,1.579130f,1.576352f,1.573574f, 1.570796f,1.568019f,1.565241f,1.562463f,1.559685f,1.556907f,1.554129f,1.551351f,1.548572f,1.545794f,1.543015f,1.540236f, 1.537457f,1.534677f,1.531898f,1.529118f,1.526337f,1.523557f,1.520775f,1.517994f,1.515212f,1.512430f,1.509647f,1.506864f, 1.504080f,1.501296f,1.498511f,1.495726f,1.492940f,1.490153f,1.487366f,1.484578f,1.481790f,1.479001f,1.476211f,1.473420f, 1.470629f,1.467837f,1.465044f,1.462250f,1.459455f,1.456660f,1.453863f,1.451066f,1.448268f,1.445469f,1.442668f,1.439867f, 1.437065f,1.434261f,1.431457f,1.428651f,1.425845f,1.423037f,1.420228f,1.417418f,1.414606f,1.411794f,1.408980f,1.406165f, 1.403348f,1.400530f,1.397711f,1.394891f,1.392069f,1.389245f,1.386420f,1.383594f,1.380766f,1.377936f,1.375105f,1.372273f, 1.369438f,1.366603f,1.363765f,1.360926f,1.358085f,1.355242f,1.352398f,1.349551f,1.346703f,1.343853f,1.341002f,1.338148f, 1.335292f,1.332434f,1.329575f,1.326713f,1.323850f,1.320984f,1.318116f,1.315246f,1.312374f,1.309500f,1.306623f,1.303745f, 1.300864f,1.297980f,1.295095f,1.292207f,1.289316f,1.286423f,1.283528f,1.280631f,1.277730f,1.274827f,1.271922f,1.269014f, 1.266104f,1.263190f,1.260275f,1.257356f,1.254434f,1.251510f,1.248583f,1.245653f,1.242720f,1.239785f,1.236846f,1.233904f, 1.230959f,1.228012f,1.225061f,1.222107f,1.219149f,1.216189f,1.213225f,1.210258f,1.207288f,1.204314f,1.201337f,1.198357f, 1.195373f,1.192385f,1.189394f,1.186400f,1.183401f,1.180399f,1.177394f,1.174384f,1.171371f,1.168354f,1.165333f,1.162308f, 1.159279f,1.156247f,1.153210f,1.150169f,1.147124f,1.144074f,1.141021f,1.137963f,1.134901f,1.131835f,1.128763f,1.125688f, 1.122608f,1.119524f,1.116435f,1.113341f,1.110242f,1.107139f,1.104031f,1.100918f,1.097800f,1.094677f,1.091549f,1.088416f, 1.085278f,1.082135f,1.078986f,1.075832f,1.072673f,1.069508f,1.066338f,1.063162f,1.059981f,1.056794f,1.053601f,1.050402f, 1.047198f,1.043987f,1.040771f,1.037548f,1.034319f,1.031084f,1.027843f,1.024596f,1.021342f,1.018081f,1.014814f,1.011541f, 1.008260f,1.004973f,1.001679f,0.998378f,0.995070f,0.991754f,0.988432f,0.985102f,0.981765f,0.978421f,0.975069f,0.971709f, 0.968342f,0.964966f,0.961583f,0.958192f,0.954793f,0.951385f,0.947970f,0.944546f,0.941113f,0.937672f,0.934222f,0.930763f, 0.927295f,0.923818f,0.920332f,0.916837f,0.913333f,0.909819f,0.906295f,0.902762f,0.899218f,0.895665f,0.892101f,0.888527f, 0.884943f,0.881349f,0.877743f,0.874127f,0.870500f,0.866862f,0.863212f,0.859551f,0.855878f,0.852194f,0.848498f,0.844789f, 0.841069f,0.837336f,0.833590f,0.829832f,0.826060f,0.822276f,0.818478f,0.814666f,0.810841f,0.807002f,0.803149f,0.799281f, 0.795399f,0.791502f,0.787590f,0.783662f,0.779719f,0.775760f,0.771785f,0.767794f,0.763786f,0.759762f,0.755720f,0.751661f, 0.747584f,0.743490f,0.739376f,0.735245f,0.731094f,0.726924f,0.722734f,0.718525f,0.714295f,0.710044f,0.705772f,0.701478f, 0.697163f,0.692825f,0.688465f,0.684081f,0.679674f,0.675242f,0.670786f,0.666305f,0.661797f,0.657264f,0.652704f,0.648116f, 0.643501f,0.638857f,0.634184f,0.629481f,0.624747f,0.619982f,0.615185f,0.610355f,0.605492f,0.600594f,0.595661f,0.590692f, 0.585685f,0.580641f,0.575558f,0.570434f,0.565269f,0.560062f,0.554811f,0.549515f,0.544173f,0.538784f,0.533345f,0.527856f, 0.522315f,0.516720f,0.511069f,0.505360f,0.499593f,0.493763f,0.487870f,0.481910f,0.475882f,0.469783f,0.463609f,0.457358f, 0.451027f,0.444612f,0.438109f,0.431514f,0.424824f,0.418034f,0.411138f,0.404131f,0.397007f,0.389761f,0.382384f,0.374869f, 0.367208f,0.359391f,0.351408f,0.343248f,0.334896f,0.326339f,0.317560f,0.308540f,0.299258f,0.289687f,0.279798f,0.269557f, 0.258921f,0.247840f,0.236251f,0.224075f,0.211211f,0.197523f,0.182829f,0.166860f,0.149209f,0.129189f,0.105458f,0.074553f, 0.000000f, }; // ArcCosのテーブル引き関数です。 inline f32 AcosTableRad(f32 x) { // 0~720へ変換 u32 index = x * 360 + 360; if (index < sizeof(acosTable)) { return acosTable[index]; } else { return 0.0f; } } #endif inline bool VEC3Normalize(nn::math::VEC3* pIn) { NN_NULL_ASSERT(pIn); f32 mag = (pIn->x * pIn->x) + (pIn->y * pIn->y) + (pIn->z * pIn->z); if (mag == 0) { return false; } mag = 1.0f / ::std::sqrtf(mag); pIn->x = pIn->x * mag; pIn->y = pIn->y * mag; pIn->z = pIn->z * mag; return true; } #include #include // ベクタ長4 バージョン asm void VEC3ArrayAddScalar(nw::math::VEC3* /*array*/, nw::math::VEC3* /*scalar*/, u32 /*countDiv12*/) { // レジスタの保存 VPUSH {d8-d11} ADD r2, #3 ASR r2, #2 // 端数処理を省けるよう、VEC3を4個ずつ一度に処理 VLDMIA r1, {s20-s22} MOV r3, r0 VEC3ArrayAddScalarLoop VLDMIA r0!, {s8-s11} VLDMIA r0!, {s12-s15} // [s8 .. s10] += [s20 .. s22] // [s11 .. s13] += [s20 .. s22] // [s14 .. s16] += [s20 .. s22] // [s17 .. s19] += [s20 .. s22] VADD.F32 s8, s8, s20 VADD.F32 s9, s9, s21 VADD.F32 s10, s10, s22 VADD.F32 s11, s11, s20 VLDMIA r0!, {s16-s19} VADD.F32 s12, s12, s21 VADD.F32 s13, s13, s22 VADD.F32 s14, s14, s20 VADD.F32 s15, s15, s21 VSTMIA r3!, {s8-s11} VADD.F32 s16, s16, s22 VADD.F32 s17, s17, s20 VADD.F32 s18, s18, s21 VADD.F32 s19, s19, s22 VSTMIA r3!, {s12-s19} SUBS r2, r2, #1 BNE VEC3ArrayAddScalarLoop // レジスタの復帰 VPOP {d8-d11} // 戻る BX lr } asm void VEC3ArrayAddVEC3Array( nw::math::VEC3* /*arrayTarget*/, // r0 nw::math::VEC3* /*arrayAdd*/, // r1 f32 /*scalar*/, // s0 u32 /*countDiv12*/ // r2 ) { // レジスタの保存 VPUSH {d8-d15} ADD r2, #3 ASR r2, #2 MOV r3, r0 // 端数処理を省けるよう、VEC3を4個ずつ一度に処理 VEC3ArrayAddVEC3ArrayLoop VLDMIA r0!, {s8-s11} VLDMIA r1!, {s20-s23} VLDMIA r0!, {s12-s15} // [s8 .. s11] += [s20 .. s23] * s0 VMLA.F32 s8, s20, s0 VMLA.F32 s9, s21, s0 VMLA.F32 s10, s22, s0 VMLA.F32 s11, s23, s0 VLDMIA r1!, {s24-s27} VSTMIA r3!, {s8-s11} // [s12 .. s15] += [s24 .. s27] * s0 VMLA.F32 s12, s24, s0 VMLA.F32 s13, s25, s0 VLDMIA r0!, {s16-s19} VMLA.F32 s14, s26, s0 VMLA.F32 s15, s27, s0 VLDMIA r1!, {s28-s31} VSTMIA r3!, {s12-s15} // [s16 .. s19] += [s28 .. s31] * s0 VMLA.F32 s16, s28, s0 VMLA.F32 s17, s29, s0 VMLA.F32 s18, s30, s0 VMLA.F32 s19, s31, s0 VSTMIA r3!, {s16-s19} SUBS r2, r2, #1 BNE VEC3ArrayAddVEC3ArrayLoop // レジスタの復帰 VPOP {d8-d15} // 戻る BX lr } asm void CalcRemainFrame( f32* /*output*/, // r0 f32* /*limitArray*/, // r1 f32 /*time*/, // s0 int /*count*/ // r2 ) { ADD r2, #7 ASR r2, #3 CalcRemainFrameLoop VLDMIA r1!, {s8-s15} // [s8 .. s15] = [s8 .. s15] + s0 VADD.F32 s8, s8, s0 VADD.F32 s9, s9, s0 VADD.F32 s10, s10, s0 VADD.F32 s11, s11, s0 VADD.F32 s12, s12, s0 VADD.F32 s13, s13, s0 VADD.F32 s14, s14, s0 VADD.F32 s15, s15, s0 VSTMIA r0!, {s8-s15} SUBS r2, r2, #1 BNE CalcRemainFrameLoop // 戻る BX lr } #include } NW_UT_RUNTIME_TYPEINFO_DEFINITION(ParticleSet, SceneNode); void ParticleSet::GetMemorySizeInternal( os::MemorySizeCalculator* pSize, ResParticleSet resNode, const ParticleSet::Description& description) { int initializerCapacity = description.maxInitializers; if (initializerCapacity < resNode.GetParticleInitializersCount()) { initializerCapacity = resNode.GetParticleInitializersCount(); } int updaterCapacity = description.maxUpdaters; if (updaterCapacity < resNode.GetParticleUpdatersCount()) { updaterCapacity = resNode.GetParticleUpdatersCount(); } os::MemorySizeCalculator& size = *pSize; size.Add(sizeof(ParticleSet), 4); size.Add(sizeof(Initializer) * initializerCapacity, 4); size.Add(sizeof(Updater) * updaterCapacity, 4); { ResParticleInitializerArray resInitializers = resNode.GetParticleInitializers(); ResParticleInitializerArrayIterator initializerEnd = resInitializers.end(); for (ResParticleInitializerArrayIterator i = resInitializers.begin(); i != initializerEnd;) { const ResParticleInitializer resInitializer = *i++; if (resInitializer.GetIsResourceCopyEnabled()) { ParticleUtil::GetMemorySizeForDuplicateResParticleInitializerInternal( pSize,&resInitializer); } } } { ResParticleUpdaterArray resUpdaters = resNode.GetParticleUpdaters(); ResParticleUpdaterArrayIterator updaterEnd = resUpdaters.end(); for (ResParticleUpdaterArrayIterator i = resUpdaters.begin(); i != updaterEnd;) { const ResParticleUpdater resUpdater = *i++; if (resUpdater.GetIsResourceCopyEnabled()) { ParticleUtil::GetMemorySizeForDuplicateResParticleUpdaterInternal( pSize, &resUpdater); } } } SceneNode::GetMemorySizeForInitialize(pSize, resNode, description); nw::gfx::ResParticleCollection resParticleCollection = resNode.GetParticleCollection(); NW_ASSERT(resParticleCollection.IsValid()); ParticleCollection::GetMemorySizeInternal(pSize, resParticleCollection); } //---------------------------------------- void ParticleSet::GetDeviceMemorySizeInternal( os::MemorySizeCalculator* pSize, ResParticleSet resNode) { nw::gfx::ResParticleCollection resParticleCollection = resNode.GetParticleCollection(); ParticleCollection::GetDeviceMemorySizeInternal(pSize, resParticleCollection); } //---------------------------------------- ParticleSet* ParticleSet::Create( SceneNode* parent, ResSceneObject resource, const ParticleSet::Description& description, os::IAllocator* mainAllocator, os::IAllocator* deviceAllocator, ParticleShape* shape ) { NW_NULL_ASSERT(mainAllocator); NW_NULL_ASSERT(deviceAllocator); ResParticleSet resNode = ResDynamicCast(resource); NW_ASSERT(resNode.IsValid()); int initializerCapacity = description.maxInitializers; if (initializerCapacity < resNode.GetParticleInitializersCount()) { initializerCapacity = resNode.GetParticleInitializersCount(); } int updaterCapacity = description.maxUpdaters; if (updaterCapacity < resNode.GetParticleUpdatersCount()) { updaterCapacity = resNode.GetParticleUpdatersCount(); } int memorySize = sizeof(ParticleSet); memorySize = ut::RoundUp(memorySize, 4); memorySize += sizeof(Initializer) * initializerCapacity; memorySize = ut::RoundUp(memorySize, 4); memorySize += sizeof(Updater) * updaterCapacity; u8* memory = reinterpret_cast(mainAllocator->Alloc(memorySize)); if (memory == NULL) { return NULL; } ParticleSet* node = new(memory) ParticleSet( mainAllocator, resNode, description); memory += sizeof(ParticleSet); { Result result = node->Initialize(mainAllocator); if (!result.IsSuccess()) { SafeDestroy(node); return NULL; } } if (parent) { bool result = parent->AttachChild(node); NW_ASSERT(result); } bool isSuccess = true; { memory = reinterpret_cast(ut::RoundUp(memory, 4)); node->m_Initializers = ut::MoveArray(memory, initializerCapacity); memory += sizeof(Initializer) * initializerCapacity; ResParticleInitializerArray resInitializers = resNode.GetParticleInitializers(); ResParticleInitializerArrayIterator initializerEnd = resInitializers.end(); for (ResParticleInitializerArrayIterator i = resInitializers.begin(); i != initializerEnd;) { const ResParticleInitializer resInitializer = *i++; Initializer initializer; if (resInitializer.GetIsResourceCopyEnabled()) { initializer.m_IsCopied = true; initializer.resource = ParticleUtil::DuplicateResParticleInitializer( &resInitializer, mainAllocator); if (initializer.resource == NULL) { isSuccess = false; } } else { initializer.m_IsCopied = false; initializer.resource = resInitializer.ptr(); } initializer.work = 0; nw::ut::ResTypeInfo resType = resInitializer.GetTypeInfo(); initializer.m_Type = resType; node->m_Initializers.push_back(initializer); } } { memory = reinterpret_cast(ut::RoundUp(memory, 4)); node->m_Updaters = ut::MoveArray(memory, updaterCapacity); memory += sizeof(Updater) * updaterCapacity; ResParticleUpdaterArray resUpdaters = resNode.GetParticleUpdaters(); ResParticleUpdaterArrayIterator updaterEnd = resUpdaters.end(); for (ResParticleUpdaterArrayIterator i = resUpdaters.begin(); i != updaterEnd;) { const ResParticleUpdater resUpdater = *i++; Updater updater; if (resUpdater.GetIsResourceCopyEnabled()) { updater.m_IsCopied = true; updater.resource = ParticleUtil::DuplicateResParticleUpdater( &resUpdater, mainAllocator); if (updater.resource == NULL) { isSuccess = false; } } else { updater.m_IsCopied = false; updater.resource = resUpdater.ptr(); } updater.work = 0; updater.m_Flags = 0; nw::ut::ResTypeInfo resType = resUpdater.GetTypeInfo(); updater.m_Type = resType; // アニメーション系のアップデータであれば、 // カーブデータがあるかこの時点で判断する。 if ((resType == ResParticleVector3AdditiveUpdater::TYPE_INFO) || (resType == ResParticleVector3ImmediateUpdater::TYPE_INFO) || (resType == ResParticleVector3RandomAdditiveUpdater::TYPE_INFO)) { ResParticleVector3Updater vec3Updater(resUpdater.ptr()); NW_ASSERT(vec3Updater.IsValid()); ResParticleAnimation animation = vec3Updater.GetParticleAnimation(); if (animation.IsValid()) { if (animation.GetAnimationData()) { updater.EnableFlags(Updater::FLAG_IS_HAS_CURVE_ANIM); } } } node->m_Updaters.push_back(updater); } } if (isSuccess == false) { SafeDestroy(node); return NULL; } nw::gfx::ResParticleCollection resParticleCollection = resNode.GetParticleCollection(); NW_ASSERT(resParticleCollection.IsValid()); ParticleCollection* collectionNode = ParticleCollection::Create( node, resParticleCollection, mainAllocator, deviceAllocator, shape); if (collectionNode == NULL) { SafeDestroy(node); return NULL; } shape->CreateCommandCache(node); // 高速化のためのストリームのポインタのキャッシュ { ut::MoveArray::iterator endIter = node->m_Initializers.end(); for (ut::MoveArray::iterator iter = node->m_Initializers.begin(); iter != endIter;) { Initializer& initializer = *iter++; const ResParticleInitializer resInitializer(initializer.resource); ParticleUsage target = resInitializer.GetTargetStream(); if (target >= 0) { if (collectionNode->GetBufferSide()) { initializer.m_TargetStreams[1] = collectionNode->GetStreamPtr(target, PARTICLE_BUFFER_FRONT); initializer.m_TargetStreams[0] = collectionNode->GetStreamPtr(target, PARTICLE_BUFFER_BACK); } else { initializer.m_TargetStreams[0] = collectionNode->GetStreamPtr(target, PARTICLE_BUFFER_FRONT); initializer.m_TargetStreams[1] = collectionNode->GetStreamPtr(target, PARTICLE_BUFFER_BACK); } } else { initializer.m_TargetStreams[0] = NULL; initializer.m_TargetStreams[1] = NULL; } } } { ut::MoveArray::iterator endIter = node->m_Updaters.end(); for (ut::MoveArray::iterator iter = node->m_Updaters.begin(); iter != endIter;) { Updater& updater = *iter++; const ResParticleUpdater resUpdater(updater.resource); ParticleUsage target = resUpdater.GetTargetStream(); if (target >= 0) { if (collectionNode->GetBufferSide()) { updater.m_TargetStreams[1] = collectionNode->GetStreamPtr(target, PARTICLE_BUFFER_FRONT); updater.m_TargetStreams[0] = collectionNode->GetStreamPtr(target, PARTICLE_BUFFER_BACK); } else { updater.m_TargetStreams[0] = collectionNode->GetStreamPtr(target, PARTICLE_BUFFER_FRONT); updater.m_TargetStreams[1] = collectionNode->GetStreamPtr(target, PARTICLE_BUFFER_BACK); } } else { updater.m_TargetStreams[0] = NULL; updater.m_TargetStreams[1] = NULL; } } } return node; } #ifdef NW_GFX_PARTICLE_COMPAT_1_1 // CALC_OPT:inline 指定へ。 /*static*/ inline void SetDefaultValues( ParticleCollection* collection, const nw::math::VEC3* positions, int particleCount, f32 time, bool isAscendingOrder ) { ParticleTime lifeParam = 1; if (!collection->IsStream(PARTICLEUSAGE_LIFE)) { lifeParam = *(ParticleTime*)collection->GetParameterPtr(PARTICLEUSAGE_LIFE); } u16* activeIndex = (u16*)collection->GetStreamPtr(PARTICLEUSAGE_ACTIVEINDEX, PARTICLE_BUFFER_FRONT); NW_NULL_ASSERT(activeIndex); const int startIndex = (isAscendingOrder)? collection->GetCount() : collection->GetCapacity() - collection->GetCount() - 1; const int incrIndex = (isAscendingOrder)? 1 : -1; u16* pFreeIndex = (u16*)collection->GetStreamPtr(PARTICLEUSAGE_FREEINDEX, PARTICLE_BUFFER_FRONT); NW_NULL_ASSERT(pFreeIndex); pFreeIndex += collection->GetCapacity() - collection->GetCount() - 1; ParticleTime* birth = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_BIRTH, PARTICLE_BUFFER_FRONT); NW_NULL_ASSERT(birth); nw::math::VEC3* translate = (nw::math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_TRANSLATE, PARTICLE_BUFFER_FRONT); NW_NULL_ASSERT(translate); nw::math::VEC3* velocity = (nw::math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_VELOCITY, PARTICLE_BUFFER_FRONT); NW_NULL_ASSERT(velocity); ParticleTime* limit = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_NEG_TIMELIMIT, PARTICLE_BUFFER_FRONT); NW_NULL_ASSERT(limit); int minIndex = collection->GetMinActiveIndex(); int maxIndex = collection->GetMaxActiveIndex(); { f32 limitDefault = -(time + lifeParam); u16* pActiveIndex = activeIndex + startIndex; for (int i = particleCount; i != 0; --i) { int nextIndex = *pFreeIndex; --pFreeIndex; *pActiveIndex = nextIndex; pActiveIndex += incrIndex; if (minIndex > nextIndex) { minIndex = nextIndex; } if (maxIndex < nextIndex) { maxIndex = nextIndex; } birth[nextIndex] = time; translate[nextIndex] = *positions++; velocity[nextIndex].x = 0.0f; velocity[nextIndex].y = 0.0f; velocity[nextIndex].z = 0.0f; limit[nextIndex] = limitDefault; } } collection->SetMinActiveIndex(minIndex); collection->SetMaxActiveIndex(maxIndex); } //---------------------------------------- void ParticleSet::AddParticles( const nw::math::MTX34& emitterMatrix, const nw::math::VEC3* const positions, ParticleSet* parentParticleSet, const u16* const parentIndices, int positionsCount ) { NW_NULL_ASSERT(positions); ParticleCollection* collection = this->GetParticleCollection(); const int collectionCount = collection->GetCount(); const int collectionCapacity = collection->GetCapacity(); int freeSize = collectionCapacity - collectionCount; if (freeSize <= 0) { return; } int particleCount = positionsCount; if (particleCount > freeSize) { particleCount = freeSize; } if (particleCount <= 0) { return; } const bool isAscendingOrder = this->IsAscendingOrder(); const int startIndex = (isAscendingOrder)? collectionCount : collectionCapacity - collectionCount - 1; const int incrIndex = (isAscendingOrder)? 1 : -1; ParticleModel* model = static_cast(this->GetParent()); NW_NULL_ASSERT(model); f32 time = model->ParticleAnimFrameController().GetFrame(); SetDefaultValues(collection, positions, particleCount, time, isAscendingOrder); this->InitializeParticles(startIndex, particleCount, incrIndex, time); collection->SetCount(collectionCount + particleCount); bool useTransform = false; if (this->WorldMatrix() != emitterMatrix) { useTransform = true; } if (parentIndices == NULL) { if (useTransform) { nw::math::MTX34 transformMatrix; nw::math::MTX34Mult(&transformMatrix, this->InverseWorldMatrix(), emitterMatrix); u16* activeIndex = (u16*)collection->GetStreamPtr(PARTICLEUSAGE_ACTIVEINDEX, PARTICLE_BUFFER_FRONT); math::VEC3* translate = (math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_TRANSLATE, PARTICLE_BUFFER_FRONT); math::VEC3* velocity = (math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_VELOCITY, PARTICLE_BUFFER_FRONT); u16* pActiveIndex = activeIndex + startIndex; for (int i = 0; i < particleCount; ++i) { int dstIndex = *pActiveIndex; pActiveIndex += incrIndex; nw::math::VEC3Transform(&translate[dstIndex], transformMatrix, translate[dstIndex]); nw::math::VEC3TransformNormal(&velocity[dstIndex], transformMatrix, velocity[dstIndex]); } } } else { math::VEC3* parentTranslate = (math::VEC3*)parentParticleSet->GetParticleCollection()->GetStreamPtr( PARTICLEUSAGE_TRANSLATE, PARTICLE_BUFFER_FRONT); u16* activeIndex = (u16*)collection->GetStreamPtr(PARTICLEUSAGE_ACTIVEINDEX, PARTICLE_BUFFER_FRONT); math::VEC3* translate = (math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_TRANSLATE, PARTICLE_BUFFER_FRONT); math::VEC3* velocity = (math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_VELOCITY, PARTICLE_BUFFER_FRONT); u16* pActiveIndex = activeIndex + startIndex; if (useTransform) { for (int i = 0; i < particleCount; ++i) { int dstIndex = *pActiveIndex; int parentIndex = parentIndices[i]; pActiveIndex += incrIndex; nw::math::MTX34 workMatrix; nw::math::MTX34 particleMatrix; nw::math::MTX34Identity(&particleMatrix); nw::math::MTX34Translate(&particleMatrix, parentTranslate[parentIndex]); ////nw::math::MTX34RotXYZRad(&workMatrix, rotate[index].x, rotate[index].y, rotate[index].z); ////nw::math::MTX34Mult(&particleMatrix, particleMatrix, workMatrix); ////nw::math::MTX34Scale(&workMatrix, scale[index]); ////nw::math::MTX34Mult(&particleMatrix, particleMatrix, workMatrix); nw::math::MTX34 transformMatrix; nw::math::MTX34Mult(&transformMatrix, this->InverseWorldMatrix(), emitterMatrix); nw::math::MTX34Mult(&workMatrix, transformMatrix, particleMatrix); nw::math::VEC3Transform(&translate[dstIndex], workMatrix, translate[dstIndex]); nw::math::VEC3TransformNormal(&velocity[dstIndex], workMatrix, velocity[dstIndex]); } } else { for (int i = 0; i < particleCount; ++i) { int dstIndex = *pActiveIndex; int parentIndex = parentIndices[i]; pActiveIndex += incrIndex; translate[dstIndex] += parentTranslate[parentIndex]; } } } } #else //---------------------------------------- int ParticleSet::AddParticles( int particleCount ) { ParticleCollection* collection = this->GetParticleCollection(); const int collectionCount = collection->GetCount(); const int collectionCapacity = collection->GetCapacity(); int freeSize = collectionCapacity - collectionCount; if (particleCount > freeSize) { particleCount = freeSize; } if (particleCount <= 0) { return 0; } const bool isAscendingOrder = this->IsAscendingOrder(); ParticleModel* model = static_cast(this->GetParent()); NW_NULL_ASSERT(model); ParticleTime time = model->ParticleAnimFrameController().GetFrame(); u16* activeIndex = (u16*)collection->GetStreamPtr(PARTICLEUSAGE_ACTIVEINDEX, PARTICLE_BUFFER_FRONT); NW_NULL_ASSERT(activeIndex); const int startIndex = (isAscendingOrder)? collection->GetCount() : collection->GetCapacity() - collection->GetCount() - 1; const int incrIndex = (isAscendingOrder)? 1 : -1; u16* pFreeIndex = (u16*)collection->GetStreamPtr(PARTICLEUSAGE_FREEINDEX, PARTICLE_BUFFER_FRONT); NW_NULL_ASSERT(pFreeIndex); pFreeIndex += collection->GetCapacity() - collection->GetCount() - 1; ParticleTime* birth = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_BIRTH, PARTICLE_BUFFER_FRONT); NW_NULL_ASSERT(birth); nw::math::VEC3* velocity = (nw::math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_VELOCITY, PARTICLE_BUFFER_FRONT); NW_NULL_ASSERT(velocity); int minIndex = collection->GetMinActiveIndex(); int maxIndex = collection->GetMaxActiveIndex(); u16* pActiveIndex = activeIndex + startIndex; for (int i = particleCount; i != 0; --i) { int nextIndex = *pFreeIndex; --pFreeIndex; *pActiveIndex = nextIndex; pActiveIndex += incrIndex; if (minIndex > nextIndex) { minIndex = nextIndex; } if (maxIndex < nextIndex) { maxIndex = nextIndex; } birth[nextIndex] = time; velocity[nextIndex].x = 0.0f; velocity[nextIndex].y = 0.0f; velocity[nextIndex].z = 0.0f; } collection->SetMinActiveIndex(minIndex); collection->SetMaxActiveIndex(maxIndex); collection->SetCount(collectionCount + particleCount); return particleCount; } #endif //---------------------------------------- void ParticleSet::InitializeParticles( int startIndex, int count, int incrIndex, ParticleTime time ) { ParticleCollection* collection = this->GetParticleCollection(); u16* activeIndex = (u16*)collection->GetStreamPtr(PARTICLEUSAGE_ACTIVEINDEX, PARTICLE_BUFFER_FRONT); activeIndex += startIndex; ut::MoveArray::iterator endIter = this->m_Initializers.end(); for (ut::MoveArray::iterator iter = this->m_Initializers.begin(); iter != endIter;) { Initializer& workInitializer = *iter++; if (workInitializer.resource == NULL) { continue; } const ResParticleInitializer initializer(workInitializer.resource); if (!initializer.GetInitializerEnabled()) { continue; } void* targetStream; if (collection->GetBufferSide()) { targetStream = workInitializer.m_TargetStreams[1]; } else { targetStream = workInitializer.m_TargetStreams[0]; } switch (workInitializer.m_Type) { case ResParticleFloatImmediateInitializer::TYPE_INFO: { NW_PARTICLE_PROFILE_START("ParticleFloatImmediateInitializer"); ResParticleFloatImmediateInitializer floatInitializer(initializer.ptr()); NW_ASSERT(floatInitializer.IsValid()); f32* storagePtr = (f32*)targetStream; const f32 value = floatInitializer.GetImmediateValue(); u16* pActiveIndex = activeIndex; for (int j = count; j != 0; --j) { int index = *pActiveIndex; pActiveIndex += incrIndex; storagePtr[index] = value; } NW_PARTICLE_PROFILE_STOP(); } break; case ResParticleVector2ImmediateInitializer::TYPE_INFO: { NW_PARTICLE_PROFILE_START("ParticleVector2ImmediateInitializer"); ResParticleVector2ImmediateInitializer vec2Initializer(initializer.ptr()); NW_ASSERT(vec2Initializer.IsValid()); nw::math::VEC2* storagePtr = (nw::math::VEC2*)targetStream; const nw::math::VEC2 value = vec2Initializer.GetImmediateValue(); u16* pActiveIndex = activeIndex; for (int j = count; j != 0; --j) { int index = *pActiveIndex; pActiveIndex += incrIndex; storagePtr[index] = value; } NW_PARTICLE_PROFILE_STOP(); } break; case ResParticleVector3ImmediateInitializer::TYPE_INFO: { NW_PARTICLE_PROFILE_START("ParticleVector3ImmediateInitializer"); ResParticleVector3ImmediateInitializer vec3Initializer(initializer.ptr()); NW_ASSERT(vec3Initializer.IsValid()); nw::math::VEC3* storagePtr = (nw::math::VEC3*)targetStream; NW_NULL_ASSERT(storagePtr); const nw::math::VEC3 value = vec3Initializer.GetImmediateValue(); u16* pActiveIndex = activeIndex; for (int j = count; j != 0; --j) { int index = *pActiveIndex; pActiveIndex += incrIndex; storagePtr[index] = value; } NW_PARTICLE_PROFILE_STOP(); } break; case ResParticleFloatRandomInitializer::TYPE_INFO: { NW_PARTICLE_PROFILE_START("ParticleFloatRandomInitializer"); ResParticleFloatRandomInitializer floatInitializer(initializer.ptr()); NW_ASSERT(floatInitializer.IsValid()); f32* storagePtr = (f32*)targetStream; NW_NULL_ASSERT(storagePtr); const f32 baseValue = floatInitializer.GetBaseValue(); const f32 random = floatInitializer.GetRandom(); u16* pActiveIndex = activeIndex; for (int j = count; j != 0; --j) { int index = *pActiveIndex; pActiveIndex += incrIndex; f32 value = baseValue; if (random != 0.0f) { value += value * this->m_ParticleRandom.NextFloatSignedOne() * random; } storagePtr[index] = value; } NW_PARTICLE_PROFILE_STOP(); } break; case ResParticleDirectionalVelocityInitializer::TYPE_INFO: { NW_PARTICLE_PROFILE_START("ParticleDirectionalVelocityInitializer"); ResParticleDirectionalVelocityInitializer velInitializer(initializer.ptr()); NW_ASSERT(velInitializer.IsValid()); nw::math::VEC3* storagePtr = (nw::math::VEC3*)targetStream; NW_NULL_ASSERT(storagePtr); const nw::math::VEC3 direction = velInitializer.GetDirection(); const f32 power = velInitializer.GetPower(); nw::math::VEC3 velocity = direction; velocity *= power; u16* pActiveIndex = activeIndex; for (int j = count; j != 0; --j) { int index = *pActiveIndex; pActiveIndex += incrIndex; storagePtr[index] += velocity; } NW_PARTICLE_PROFILE_STOP(); } break; case ResParticleRandomDirectionalVelocityInitializer::TYPE_INFO: { NW_PARTICLE_PROFILE_START("ParticleRandomDirectionalVelocityInitializer"); ResParticleRandomDirectionalVelocityInitializer velInitializer(initializer.ptr()); NW_ASSERT(velInitializer.IsValid()); nw::math::VEC3* storagePtr = (nw::math::VEC3*)targetStream; NW_NULL_ASSERT(storagePtr); const nw::math::VEC3 direction = velInitializer.GetDirection(); const f32 power = velInitializer.GetPower(); const f32 angle = velInitializer.GetAngle(); f32 sin, cos; // 変換行列のパラメータを計算 f32 sx = direction.z; NW_ASSERT(sx <= 1.0f); f32 cx = 1.0f - sx * sx; f32 divcx; if (cx >= 0.0f) { divcx = math::FrSqrt(cx); } else { // 誤差等の対策 divcx = 0.0f; } cx = cx * divcx; f32 sz; f32 cz; if (cx == 0.0f) { sz = 0; cz = 1; } else { sz = direction.x * -divcx; cz = direction.y * divcx; } f32 sxsz = sx * sz; f32 msxcz = -sx * cz; // (0,1,0)を基準にランダムを適用した円錐状ベクトルを生成する nw::math::VEC3 velocity; u16* pActiveIndex = activeIndex; for (int j = count; j != 0; --j) { nn::math::SinCosRad(&sin, &cos, nw::math::F_PI/2.f - (angle * this->m_ParticleRandom.NextFloatSignedOne())); velocity.x = cos; velocity.y = sin; velocity.z = 0.0f; f32 rad = velocity.x; nn::math::SinCosRad(&sin, &cos, 2.0f * nw::math::F_PI * this->m_ParticleRandom.NextFloatSignedOne()); velocity.x = rad * cos; velocity.z = rad * sin; nw::math::VEC3 tempv; tempv.x = velocity.x * cz + velocity.y * direction.x + velocity.z * sxsz; tempv.y = velocity.x * sz + velocity.y * direction.y + velocity.z * msxcz; tempv.z = velocity.y * direction.z + velocity.z * cx; velocity = tempv * power; int index = *pActiveIndex; pActiveIndex += incrIndex; storagePtr[index] += velocity; } NW_PARTICLE_PROFILE_STOP(); } break; case ResParticleOriginVelocityInitializer::TYPE_INFO: { NW_PARTICLE_PROFILE_START("ParticleOriginVelocityInitializer"); ResParticleOriginVelocityInitializer velInitializer(initializer.ptr()); NW_ASSERT(velInitializer.IsValid()); nw::math::VEC3* storagePtr = (nw::math::VEC3*)targetStream; NW_NULL_ASSERT(storagePtr); nw::math::VEC3* trans = (nw::math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_TRANSLATE, PARTICLE_BUFFER_FRONT); NW_NULL_ASSERT(trans); const f32 power = velInitializer.GetPower(); u16* pActiveIndex = activeIndex; for (int j = count; j != 0; --j) { int index = *pActiveIndex; pActiveIndex += incrIndex; nw::math::VEC3 velocity = trans[index]; velocity.SafeNormalize(nw::math::VEC3(0, 0, 0)); velocity *= power; storagePtr[index] += velocity; } NW_PARTICLE_PROFILE_STOP(); } break; case ResParticleRandomVelocityInitializer::TYPE_INFO: { NW_PARTICLE_PROFILE_START("ParticleRandomVelocityInitializer"); ResParticleRandomVelocityInitializer velInitializer(initializer.ptr()); NW_ASSERT(velInitializer.IsValid()); nw::math::VEC3* storagePtr = (nw::math::VEC3*)targetStream; NW_NULL_ASSERT(storagePtr); const f32 power = velInitializer.GetPower(); u16* pActiveIndex = activeIndex; for (int j = count; j != 0; --j) { int index = *pActiveIndex; pActiveIndex += incrIndex; nw::math::VEC3 velocity(0.0f, 0.0f, 0.0f); f32 yaw = this->m_ParticleRandom.NextFloat() * 2.0f * nw::math::F_PI; f32 pitch = this->m_ParticleRandom.NextFloatSignedHalf() * nw::math::F_PI; f32 sinYaw = 0.0f; f32 cosYaw = 1.0f; if (yaw != 0.0f) { nw::math::SinCosRad(&sinYaw, &cosYaw, yaw); } f32 sinPitch = 0.0f; f32 cosPitch = 1.0f; if (pitch != 0.0f) { nw::math::SinCosRad(&sinPitch, &cosPitch, pitch); } velocity.x = sinYaw * -cosPitch; velocity.y = -sinPitch; velocity.z = cosYaw * cosPitch; velocity *= power; storagePtr[index] += velocity; } NW_PARTICLE_PROFILE_STOP(); } break; case ResParticleYAxisVelocityInitializer::TYPE_INFO: { NW_PARTICLE_PROFILE_START("ParticleYAxisVelocityInitializer"); ResParticleYAxisVelocityInitializer velInitializer(initializer.ptr()); NW_ASSERT(velInitializer.IsValid()); nw::math::VEC3* storagePtr = (nw::math::VEC3*)targetStream; NW_NULL_ASSERT(storagePtr); nw::math::VEC3* trans = (nw::math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_TRANSLATE, PARTICLE_BUFFER_FRONT); const f32 power = velInitializer.GetPower(); u16* pActiveIndex = activeIndex; for (int j = count; j != 0; --j) { int index = *pActiveIndex; pActiveIndex += incrIndex; nw::math::VEC3 velocity(trans[index].x, 0.0f, trans[index].z); velocity.SafeNormalize(nw::math::VEC3(0, 0, 0)); velocity *= power; storagePtr[index] += velocity; } NW_PARTICLE_PROFILE_STOP(); } break; case ResParticleVector3Random1Initializer::TYPE_INFO: { NW_PARTICLE_PROFILE_START("ParticleVector3Random1Initializer"); ResParticleVector3Random1Initializer vec3Initializer(initializer.ptr()); NW_ASSERT(vec3Initializer.IsValid()); nw::math::VEC3* storagePtr = (nw::math::VEC3*)targetStream; NW_NULL_ASSERT(storagePtr); const nw::math::VEC3 baseValue = vec3Initializer.GetBaseValue(); const f32 random = vec3Initializer.GetRandom(); u16* pActiveIndex = activeIndex; for (int j = count; j != 0; --j) { int index = *pActiveIndex; pActiveIndex += incrIndex; nw::math::VEC3 value(baseValue); if (random != 0.0f) { f32 floatRandom = this->m_ParticleRandom.NextFloatSignedOne(); floatRandom *= random; value.x += value.x * floatRandom; value.y += value.y * floatRandom; value.z += value.z * floatRandom; } storagePtr[index] = value; } NW_PARTICLE_PROFILE_STOP(); } break; case ResParticleVector3Random3Initializer::TYPE_INFO: { NW_PARTICLE_PROFILE_START("ParticleVector3Random3Initializer"); ResParticleVector3Random3Initializer vec3Initializer(initializer.ptr()); NW_ASSERT(vec3Initializer.IsValid()); nw::math::VEC3* storagePtr = (nw::math::VEC3*)targetStream; NW_NULL_ASSERT(storagePtr); const nw::math::VEC3 baseValue = vec3Initializer.GetBaseValue(); const nw::math::VEC3 random = vec3Initializer.GetRandom(); u16* pActiveIndex = activeIndex; for (int j = count; j != 0; --j) { int index = *pActiveIndex; pActiveIndex += incrIndex; nw::math::VEC3 value(baseValue); if (random.x != 0.0f) { f32 floatRandom = this->m_ParticleRandom.NextFloatSignedOne(); floatRandom *= random.x; value.x += value.x * floatRandom; } if (random.y != 0.0f) { f32 floatRandom = this->m_ParticleRandom.NextFloatSignedOne(); floatRandom *= random.y; value.y += value.y * floatRandom; } if (random.z != 0.0f) { f32 floatRandom = this->m_ParticleRandom.NextFloatSignedOne(); floatRandom *= random.z; value.z += value.z * floatRandom; } storagePtr[index] = value; } NW_PARTICLE_PROFILE_STOP(); } break; case ResParticleVector3MultRandomInitializer::TYPE_INFO: { NW_PARTICLE_PROFILE_START("ParticleVector3MultRandomInitializer"); ResParticleVector3MultRandomInitializer vec3Initializer(initializer.ptr()); NW_ASSERT(vec3Initializer.IsValid()); nw::math::VEC3* storagePtr = (nw::math::VEC3*)targetStream; NW_NULL_ASSERT(storagePtr); const f32 random = vec3Initializer.GetRandom(); u16* pActiveIndex = activeIndex; for (int j = count; j != 0; --j) { int index = *pActiveIndex; pActiveIndex += incrIndex; f32 floatRandom = this->m_ParticleRandom.NextFloatSignedOne(); floatRandom = 1.0f + (floatRandom * random); storagePtr[index].x *= floatRandom; storagePtr[index].y *= floatRandom; storagePtr[index].z *= floatRandom; } NW_PARTICLE_PROFILE_STOP(); } break; case ResParticleFloatRangeRandomInitializer::TYPE_INFO: { NW_PARTICLE_PROFILE_START("ParticleFloatRangeRandomInitializer"); ResParticleFloatRangeRandomInitializer floatInitializer(initializer.ptr()); NW_ASSERT(floatInitializer.IsValid()); #ifdef NW_GFX_PARTICLE_COMPAT_1_1 ParticleTime* limit = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_NEG_TIMELIMIT, PARTICLE_BUFFER_FRONT); #endif ParticleTime* storagePtr = (ParticleTime*)targetStream; NW_NULL_ASSERT(storagePtr); f32 minValue = floatInitializer.GetMinValue(); f32 maxValue = floatInitializer.GetMaxValue(); #if 1 NW_ASSERT(minValue <= maxValue); #else if (minValue > maxValue) { f32 swap = minValue; minValue = maxValue; maxValue = swap; } #endif f32 diff = maxValue - minValue; u16* pActiveIndex = activeIndex; for (int j = count; j != 0; --j) { int index = *pActiveIndex; pActiveIndex += incrIndex; f32 value = minValue + this->m_ParticleRandom.Next((int)(diff + 1)); storagePtr[index] = value; #ifdef NW_GFX_PARTICLE_COMPAT_1_1 limit[index] = -(time + value); #endif } NW_PARTICLE_PROFILE_STOP(); } break; } } } //---------------------------------------- //! @brief アニメーションで参照すべきテーブルの位置を計算します。 //! @details :private //! //! @param[in] animationOption アニメーションオプションです。 //! @param[in] id パーティクルのIDです。 //! @param[in] birth パーティクルの生まれた時刻です。 //! @param[in] life パーティクルの寿命です。 //! @param[in] time 現在の時刻です。 //! @param[in] animationLength アニメーションの長さです。 //! @param[out] interp 次のキーと補間すべきかのフラグです。 //! @param[out] interpFactor 次のキーと補間する際の係数です。0で補間なしです。 //! @param[out] updaterIndex アップデータのインデックス番号です。 //! // CALC_OPT:inline 指定へ。 /*static*/inline int CalcAnimationDataIndex( const ResParticleAnimationOption& animationOption, u32 id, ParticleTime birth, ParticleTime life, ParticleTime time, int animationLength, bool* interp, f32* interpFactor, int updaterIndex ) { NW_NULL_ASSERT(interp); NW_NULL_ASSERT(interpFactor); ParticleTime ptclTime = animationOption.EvaluateAnimationFrame(id, birth, life, time, updaterIndex); #ifdef NW_GFX_PARTICLE_COMPAT_1_1 if (ptclTime < 0) #else if (ptclTime.GetParticleTimeValue() < 0) #endif { ptclTime = 0; } if (ptclTime >= animationLength) { ptclTime = (f32)animationLength - 1; } #ifdef NW_GFX_PARTICLE_COMPAT_1_1 int index = (int)ptclTime; *interpFactor = ptclTime - index; #else int index = ptclTime.GetIntegralParts(); *interpFactor = ptclTime.GetFractionalParts(); #endif if (*interpFactor != 0.0f) { *interp = true; } else { *interp = false; } return index; } void ParticleAccelarationUpdater( const ResParticleUpdaterData* updater, ParticleCollection* collection, f32 diffTime ) { NW_PARTICLE_PROFILE_START("ParticleAccelarationUpdater"); ResParticleAccelarationUpdater accelUpdater(updater); NW_ASSERT(accelUpdater.IsValid()); ParticleUsage target = accelUpdater.GetTargetStream(); nw::math::VEC3* storagePtr = (nw::math::VEC3*)collection->GetStreamPtr(target, PARTICLE_BUFFER_FRONT); NW_NULL_ASSERT(storagePtr); // factor = 1 - a として // 1 - difftime * a と近似します(粗めです)。 f32 factor = 1.0f - diffTime * (1.0f - accelUpdater.GetFactor()); const u32 maxIndex = collection->GetMaxActiveIndex() + 1; for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index) { storagePtr[index] *= factor; } NW_PARTICLE_PROFILE_STOP(); } void ParticleFloatImmediateUpdater( const ResParticleUpdaterData* updater, ParticleCollection* collection, ParticleTime time, int updaterIndex ) { NW_PARTICLE_PROFILE_START("ParticleFloatImmediateUpdater"); ParticleTime* birth = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_BIRTH, PARTICLE_BUFFER_FRONT); ParticleTime* life; ParticleTime lifeParam = 1.0f; collection->GetStreamOrParameter(PARTICLEUSAGE_LIFE, &life, &lifeParam, PARTICLE_BUFFER_FRONT); int animationLength = 0; //int animationDimension = 0; //int animationStride = 0; bool* animationEnable = NULL; f32* animationData = NULL; ResParticleFloatImmediateUpdater floatUpdater(updater); NW_ASSERT(floatUpdater.IsValid()); ParticleUsage target = floatUpdater.GetTargetStream(); f32* storagePtr = (f32*)collection->GetStreamPtr(target, PARTICLE_BUFFER_FRONT); NW_NULL_ASSERT(storagePtr); ResParticleAnimation animation = floatUpdater.GetParticleAnimation(); if (animation.IsValid()) { animationLength = animation.GetAnimationLength(); //animationDimension = animation.GetAnimationDimension(); //animationStride = animation.GetAnimationStride(); animationEnable = animation.GetAnimationEnabled(); animationData = animation.GetAnimationData(); } if (animationData == NULL || !animationEnable[0]) { const f32 baseValue = floatUpdater.GetDefaultValue(); const u32 maxIndex = collection->GetMaxActiveIndex() + 1; for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index) { storagePtr[index] = baseValue; } } else { const u32 maxIndex = collection->GetMaxActiveIndex() + 1; for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index) { bool interp; f32 interpFactor; int animationDataIndex; ResParticleAnimationOption animationOption = animation.GetParticleAnimationOption(); animationDataIndex = CalcAnimationDataIndex( animationOption, index, birth[index], (life == NULL) ? lifeParam : life[index], time, animationLength, &interp, &interpFactor, updaterIndex); //animationDataIndex *= animationStride; f32 value = animationData[animationDataIndex++]; if (interp) { f32 nextValue = animationData[animationDataIndex++]; value = value + (nextValue - value) * interpFactor; } storagePtr[index] = value; } } NW_PARTICLE_PROFILE_STOP(); } #ifdef NW_GFX_PARTICLE_4KEY void ParticleFloatImmediate4KeyUpdater( const ResParticleUpdaterData* updater, ParticleCollection* collection, ParticleTime time ) { NW_PARTICLE_PROFILE_START("ParticleFloatImmediate4KeyUpdater"); ParticleTime* birth = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_BIRTH, PARTICLE_BUFFER_FRONT); ParticleTime* life; ParticleTime lifeParam = 1.0f; collection->GetStreamOrParameter(PARTICLEUSAGE_LIFE, &life, &lifeParam, PARTICLE_BUFFER_FRONT); ResParticleFloatImmediate4KeyUpdater fkeyUpdater(updater); NW_ASSERT(fkeyUpdater.IsValid()); ParticleUsage target = fkeyUpdater.GetTargetStream(); f32* storagePtr = (f32*)collection->GetStreamPtr(target, PARTICLE_BUFFER_FRONT); NW_NULL_ASSERT(storagePtr); register u32 inTime = fkeyUpdater.GetInTime(); register u32 outTime = fkeyUpdater.GetOutTime(); register f32 value0 = fkeyUpdater.GetValue0(); register f32 value1 = fkeyUpdater.GetValue1(); register f32 value2 = fkeyUpdater.GetValue2(); register f32 value3 = fkeyUpdater.GetValue3(); if (life == NULL) { u32 flife = 0x10000 / lifeParam.GetFloat32Value(); const u32 maxIndex = collection->GetMaxActiveIndex() + 1; for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index) { ParticleTime difftime = time - birth[index]; u32 ftime = (u32)(difftime.GetFloat32Value() * flife); f32 value; if (ftime < inTime) { value = value0 * ftime + value1; } else if (ftime < outTime) { value = value1; } else { value = value2 * ftime + value3; } storagePtr[index] = value; } } else { const u32 maxIndex = collection->GetMaxActiveIndex() + 1; for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index) { f32 flife = 1.0f / life[index].GetFloat32Value(); ParticleTime difftime = time - birth[index]; u32 ftime = difftime.GetFloat32Value() * flife * 0x10000; f32 value; if (ftime < inTime) { value = value0 * ftime + value1; } else if (ftime < outTime) { value = value1; } else { value = value2 * ftime + value3; } storagePtr[index] = value; } } NW_PARTICLE_PROFILE_STOP(); } #endif void ParticleGravityUpdater( nw::math::VEC3* storagePtr, const ResParticleUpdaterData* updater, ParticleCollection* collection, f32 diffTime ) { NW_PARTICLE_PROFILE_START("ParticleGravityUpdater"); NW_NULL_ASSERT(storagePtr); ResParticleGravityUpdater gravUpdater(updater); NW_ASSERT(gravUpdater.IsValid()); nw::math::VEC3 velocity = gravUpdater.GetDirection(); const f32 power = gravUpdater.GetPower() * diffTime; #ifdef NW_GFX_PARTICLE_COMPAT_1_1 velocity.SafeNormalize(nw::math::VEC3(0, -1, 0)); #endif velocity *= power; // 存在しないパーティクルもかまわず処理することで、スループットを上げる storagePtr += collection->GetMinActiveIndex(); const int count = collection->GetMaxActiveIndex() - collection->GetMinActiveIndex() + 1; internal::VEC3ArrayAddScalar(storagePtr, &velocity, count); NW_PARTICLE_PROFILE_STOP(); } void ParticleSpinUpdater( const ResParticleUpdaterData* updater, ParticleCollection* collection, f32 diffTime ) { NW_PARTICLE_PROFILE_START("ParticleSpinUpdater"); ResParticleSpinUpdater spinUpdater(updater); NW_ASSERT(spinUpdater.IsValid()); ParticleUsage target = spinUpdater.GetTargetStream(); nw::math::VEC3* storagePtr = (nw::math::VEC3*)collection->GetStreamPtr(target, PARTICLE_BUFFER_FRONT); NW_NULL_ASSERT(storagePtr); const nw::math::VEC3 axis = spinUpdater.GetAxis(); const f32 power = spinUpdater.GetPower() * diffTime; if (power != 0.0f && !axis.IsZero()) { nw::math::MTX34 matrix; nw::math::MTX34RotAxisRad(&matrix, &axis, power); nw::math::VEC3 value; const u32 maxIndex = collection->GetMaxActiveIndex() + 1; for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index) { nw::math::VEC3TransformNormal(&value, matrix, storagePtr[index]); // CALC_OPT:展開することで、valueが確定する前からコピーが始まる。 // storagePtr[index] = value; storagePtr[index].x = value.x; storagePtr[index].y = value.y; storagePtr[index].z = value.z; } } NW_PARTICLE_PROFILE_STOP(); } void ParticleRandomUpdater( const ResParticleUpdaterData* updater, ParticleCollection* collection, ParticleTime time, ParticleTime prevTime, ParticleRandom& random ) { NW_PARTICLE_PROFILE_START("ParticleRandomUpdater"); ParticleTime* birth = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_BIRTH, PARTICLE_BUFFER_FRONT); ParticleTime* life; ParticleTime lifeParam = 1.0f; collection->GetStreamOrParameter(PARTICLEUSAGE_LIFE, &life, &lifeParam, PARTICLE_BUFFER_FRONT); ResParticleRandomUpdater randomUpdater(updater); NW_ASSERT(randomUpdater.IsValid()); ParticleUsage target = randomUpdater.GetTargetStream(); nw::math::VEC3* storagePtr = (nw::math::VEC3*)collection->GetStreamPtr(target, PARTICLE_BUFFER_FRONT); NW_NULL_ASSERT(storagePtr); #ifdef NW_GFX_PARTICLE_COMPAT_1_1 f32 processCount = math::FFloor(time) - math::FCeil(prevTime) + 1; #else f32 processCount = time.Floor() - prevTime.Ceil() + 1; #endif if (processCount > 0) // 整数を跨いでいないならいずれにしろ実行することはない { const s32 interval = randomUpdater.GetInterval(); if (interval <= 1) // 毎フレーム { const nw::math::VEC3 power = randomUpdater.GetPower() * processCount; const u32 maxIndex = collection->GetMaxActiveIndex() + 1; for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index) { storagePtr[index].x += power.x * random.NextFloatSignedOne(); storagePtr[index].y += power.y * random.NextFloatSignedOne(); storagePtr[index].z += power.z * random.NextFloatSignedOne(); } } else // インターバルあり { const nw::math::VEC3 power = randomUpdater.GetPower() * processCount; nw::math::VEC3 value; const u32 maxIndex = collection->GetMaxActiveIndex() + 1; for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index) { // 粒子ごとに再計算 #ifdef NW_GFX_PARTICLE_COMPAT_1_1 processCount = math::FFloor((time - birth[index]) / interval) - math::FCeil((prevTime - birth[index]) / interval) + 1; #else ParticleTime time_birth = time - birth[index]; ParticleTime prev_birth = prevTime - birth[index]; processCount = math::FFloor(time_birth.GetFloat32Value() / interval) - math::FCeil(prev_birth.GetFloat32Value() / interval) + 1; #endif if (processCount > 0) { storagePtr[index].x += power.x * random.NextFloatSignedOne() * processCount; storagePtr[index].y += power.y * random.NextFloatSignedOne() * processCount; storagePtr[index].z += power.z * random.NextFloatSignedOne() * processCount; } } } } NW_PARTICLE_PROFILE_STOP(); } void ParticleVector2ImmediateUpdater( const ResParticleUpdaterData* updater, ParticleCollection* collection, ParticleTime time, int updaterIndex ) { NW_PARTICLE_PROFILE_START("ParticleVector2ImmediateUpdater"); ParticleTime* birth = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_BIRTH, PARTICLE_BUFFER_FRONT); ParticleTime* life; ParticleTime lifeParam = 1.0f; collection->GetStreamOrParameter(PARTICLEUSAGE_LIFE, &life, &lifeParam, PARTICLE_BUFFER_FRONT); int animationLength = 0; //int animationDimension = 0; int animationStride = 0; bool* animationEnable = NULL; f32* animationData = NULL; ResParticleVector2ImmediateUpdater vec2Updater(updater); NW_ASSERT(vec2Updater.IsValid()); ResParticleAnimation animation = vec2Updater.GetParticleAnimation(); if (animation.IsValid()) { animationLength = animation.GetAnimationLength(); //animationDimension = animation.GetAnimationDimension(); animationStride = animation.GetAnimationStride(); animationEnable = animation.GetAnimationEnabled(); animationData = animation.GetAnimationData(); } ParticleUsage target = vec2Updater.GetTargetStream(); nw::math::VEC2* storagePtr = (nw::math::VEC2*)collection->GetStreamPtr(target, PARTICLE_BUFFER_FRONT); NW_NULL_ASSERT(storagePtr); const nw::math::VEC2 defaultValue = vec2Updater.GetDefaultValue(); const u32 maxIndex = collection->GetMaxActiveIndex() + 1; for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index) { nw::math::VEC2 value = defaultValue; bool interp; f32 interpFactor; int animationDataIndex; if (animationData != NULL) { ResParticleAnimationOption animationOption = animation.GetParticleAnimationOption(); animationDataIndex = CalcAnimationDataIndex( animationOption, index, birth[index], (life == NULL) ? lifeParam : life[index], time, animationLength, &interp, &interpFactor, updaterIndex); animationDataIndex *= animationStride; if (animationEnable[0]) { value.x = animationData[animationDataIndex++]; } if (animationEnable[1]) { value.y = animationData[animationDataIndex++]; } if (interp) { if (animationEnable[0]) { f32 nextValue = animationData[animationDataIndex++]; value.x = value.x + (nextValue - value.x) * interpFactor; } if (animationEnable[1]) { f32 nextValue = animationData[animationDataIndex++]; value.y = value.y + (nextValue - value.y) * interpFactor; } } } storagePtr[index] = value; } NW_PARTICLE_PROFILE_STOP(); } void ParticleVector3AdditiveUpdater( const ResParticleUpdaterData* updater, ParticleCollection* collection, ParticleTime time, f32 diffTime, int updaterIndex ) { ParticleTime* birth = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_BIRTH, PARTICLE_BUFFER_FRONT); ParticleTime* life; ParticleTime lifeParam = 1.0f; collection->GetStreamOrParameter(PARTICLEUSAGE_LIFE, &life, &lifeParam, PARTICLE_BUFFER_FRONT); int animationLength = 0; //int animationDimension = 0; int animationStride = 0; bool* animationEnable = NULL; f32* animationData = NULL; ResParticleVector3AdditiveUpdater vec3Updater(updater); NW_ASSERT(vec3Updater.IsValid()); ParticleUsage target = vec3Updater.GetTargetStream(); nw::math::VEC3* storagePtr = (nw::math::VEC3*)collection->GetStreamPtr(target, PARTICLE_BUFFER_FRONT); NW_NULL_ASSERT(storagePtr); const nw::math::VEC3 defaultValue = vec3Updater.GetDefaultValue(); ResParticleAnimation animation = vec3Updater.GetParticleAnimation(); if (animation.IsValid()) { animationLength = animation.GetAnimationLength(); //animationDimension = animation.GetAnimationDimension(); animationStride = animation.GetAnimationStride(); animationEnable = animation.GetAnimationEnabled(); animationData = animation.GetAnimationData(); } const u32 maxIndex = collection->GetMaxActiveIndex() + 1; for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index) { // CALC_OPT:コピーをわけることで、適切なタイミングでコピーが実行される模様。 register nw::math::VEC3 value; value.x = defaultValue.x; value.y = defaultValue.y; value.z = defaultValue.z; bool interp; f32 interpFactor; int animationDataIndex; // CALC_OPT:カーブ無しデータはここまで処理がこない。 // if (animationData != NULL) { ResParticleAnimationOption animationOption = animation.GetParticleAnimationOption(); animationDataIndex = CalcAnimationDataIndex( animationOption, index, birth[index], (life == NULL) ? lifeParam : life[index], time, animationLength, &interp, &interpFactor, updaterIndex); animationDataIndex *= animationStride; if (animationEnable[0]) { value.x = animationData[animationDataIndex++]; } if (animationEnable[1]) { value.y = animationData[animationDataIndex++]; } if (animationEnable[2]) { value.z = animationData[animationDataIndex++]; } if (interp) { if (animationEnable[0]) { f32 nextValue = animationData[animationDataIndex++]; value.x = value.x + (nextValue - value.x) * interpFactor; } if (animationEnable[1]) { f32 nextValue = animationData[animationDataIndex++]; value.y = value.y + (nextValue - value.y) * interpFactor; } if (animationEnable[2]) { f32 nextValue = animationData[animationDataIndex++]; value.z = value.z + (nextValue - value.z) * interpFactor; } } } storagePtr[index].x += value.x * diffTime; storagePtr[index].y += value.y * diffTime; storagePtr[index].z += value.z * diffTime; } } void ParticleVector3ImmediateUpdater( const ResParticleUpdaterData* updater, ParticleCollection* collection, ParticleTime time, int updaterIndex ) { ParticleTime* birth = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_BIRTH, PARTICLE_BUFFER_FRONT); ParticleTime* life; ParticleTime lifeParam = 1.0f; collection->GetStreamOrParameter(PARTICLEUSAGE_LIFE, &life, &lifeParam, PARTICLE_BUFFER_FRONT); int animationLength = 0; //int animationDimension = 0; int animationStride = 0; bool* animationEnable = NULL; f32* animationData = NULL; ResParticleVector3ImmediateUpdater vec3Updater(updater); NW_ASSERT(vec3Updater.IsValid()); ResParticleAnimation animation = vec3Updater.GetParticleAnimation(); if (animation.IsValid()) { animationLength = animation.GetAnimationLength(); //animationDimension = animation.GetAnimationDimension(); animationStride = animation.GetAnimationStride(); animationEnable = animation.GetAnimationEnabled(); animationData = animation.GetAnimationData(); } ParticleUsage target = vec3Updater.GetTargetStream(); nw::math::VEC3* storagePtr = (nw::math::VEC3*)collection->GetStreamPtr(target, PARTICLE_BUFFER_FRONT); NW_NULL_ASSERT(storagePtr); const nw::math::VEC3 defaultValue = vec3Updater.GetDefaultValue(); const u32 maxIndex = collection->GetMaxActiveIndex() + 1; for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index) { nw::math::VEC3 value = defaultValue; bool interp; f32 interpFactor; int animationDataIndex; // CALC_OPT:カーブ無しデータはここまで処理がこない。 // if (animationData != NULL) { ResParticleAnimationOption animationOption = animation.GetParticleAnimationOption(); animationDataIndex = CalcAnimationDataIndex( animationOption, index, birth[index], (life == NULL) ? lifeParam : life[index], time, animationLength, &interp, &interpFactor, updaterIndex); animationDataIndex *= animationStride; if (animationEnable[0]) { value.x = animationData[animationDataIndex++]; } if (animationEnable[1]) { value.y = animationData[animationDataIndex++]; } if (animationEnable[2]) { value.z = animationData[animationDataIndex++]; } if (interp) { if (animationEnable[0]) { f32 nextValue = animationData[animationDataIndex++]; value.x = value.x + (nextValue - value.x) * interpFactor; } if (animationEnable[1]) { f32 nextValue = animationData[animationDataIndex++]; value.y = value.y + (nextValue - value.y) * interpFactor; } if (animationEnable[2]) { f32 nextValue = animationData[animationDataIndex++]; value.z = value.z + (nextValue - value.z) * interpFactor; } } } storagePtr[index] = value; } } void ParticleVector3RandomAdditiveUpdater( const ResParticleUpdaterData* updater, ParticleCollection* collection, ParticleTime time, f32 diffTime, int updaterIndex ) { ParticleTime* birth = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_BIRTH, PARTICLE_BUFFER_FRONT); ParticleTime* life; ParticleTime lifeParam = 1.0f; collection->GetStreamOrParameter(PARTICLEUSAGE_LIFE, &life, &lifeParam, PARTICLE_BUFFER_FRONT); int animationLength = 0; //int animationDimension = 0; int animationStride = 0; bool* animationEnable = NULL; f32* animationData = NULL; ResParticleVector3RandomAdditiveUpdater vec3Updater(updater); NW_ASSERT(vec3Updater.IsValid()); ResParticleAnimation animation = vec3Updater.GetParticleAnimation(); if (animation.IsValid()) { animationLength = animation.GetAnimationLength(); //animationDimension = animation.GetAnimationDimension(); animationStride = animation.GetAnimationStride(); animationEnable = animation.GetAnimationEnabled(); animationData = animation.GetAnimationData(); } ParticleUsage target = vec3Updater.GetTargetStream(); nw::math::VEC3* storagePtr = (nw::math::VEC3*)collection->GetStreamPtr(target, PARTICLE_BUFFER_FRONT); NW_NULL_ASSERT(storagePtr); const nw::math::VEC3 defaultValue = vec3Updater.GetDefaultValue(); const u32 maxIndex = collection->GetMaxActiveIndex() + 1; for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index) { nw::math::VEC3 value = defaultValue; bool interp; f32 interpFactor; int animationDataIndex; // CALC_OPT:カーブ無しデータはここまで処理がこない。 // if (animationData != NULL) { ResParticleAnimationOption animationOption = animation.GetParticleAnimationOption(); animationDataIndex = CalcAnimationDataIndex( animationOption, index, birth[index], (life == NULL) ? lifeParam : life[index], time, animationLength, &interp, &interpFactor, updaterIndex); animationDataIndex *= animationStride; if (animationEnable[0]) { value.x = animationData[animationDataIndex++]; } if (animationEnable[1]) { value.y = animationData[animationDataIndex++]; } if (animationEnable[2]) { value.z = animationData[animationDataIndex++]; } if (interp) { if (animationEnable[0]) { f32 nextValue = animationData[animationDataIndex++]; value.x = value.x + (nextValue - value.x) * interpFactor; } if (animationEnable[1]) { f32 nextValue = animationData[animationDataIndex++]; value.y = value.y + (nextValue - value.y) * interpFactor; } if (animationEnable[2]) { f32 nextValue = animationData[animationDataIndex++]; value.z = value.z + (nextValue - value.z) * interpFactor; } } } storagePtr[index] = value * diffTime; } } void ParticleRotateUpVectorUpdater( const ResParticleUpdaterData* updater, ParticleCollection* collection ) { NW_PARTICLE_PROFILE_START("ParticleRotateUpVectorUpdater"); ResParticleRotateUpVectorUpdater vec3Updater(updater); NW_ASSERT(vec3Updater.IsValid()); NW_ASSERT(vec3Updater.GetTargetStream() == PARTICLEUSAGE_ROTATE); nw::math::VEC3* storagePtr = (nw::math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_ROTATE, PARTICLE_BUFFER_FRONT); NW_NULL_ASSERT(storagePtr); ResParticleRotateUpVectorUpdater::ParticleRotateUpVectorSource source = vec3Updater.GetSource(); switch (source) { case ResParticleRotateUpVectorUpdater::SOURCE_VELOCITY: { nw::math::VEC3* srcStoragePtr = (nw::math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_VELOCITY, PARTICLE_BUFFER_FRONT); const u32 maxIndex = collection->GetMaxActiveIndex() + 1; for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index) { nw::math::VEC3 ySrcAxis = srcStoragePtr[index]; if (internal::VEC3Normalize(&ySrcAxis)) { // CALC_OPT:acosをテーブル引きに。 // 2010/09/27 一旦、精度上の問題で戻しました。 storagePtr[index].x = nw::math::AcosRad(ySrcAxis.y); // storagePtr[index].x = internal::AcosTableRad(ySrcAxis.y); // nw::math::Atan2Rad よりも std::atan2fの方が速い(SDK0.13) storagePtr[index].y = ::std::atan2f(ySrcAxis.x, ySrcAxis.z); storagePtr[index].z = 0; } } } break; case ResParticleRotateUpVectorUpdater::SOURCE_DISTANCE: { nw::math::VEC3* srcStoragePtr = (nw::math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_TRANSLATE, PARTICLE_BUFFER_FRONT); nw::math::VEC3* optionStoragePtr = (nw::math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_TRANSLATE, PARTICLE_BUFFER_BACK); const u32 maxIndex = collection->GetMaxActiveIndex() + 1; for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index) { nw::math::VEC3 ySrcAxis = srcStoragePtr[index]; ySrcAxis -= optionStoragePtr[index]; if (internal::VEC3Normalize(&ySrcAxis)) { // CALC_OPT:acosをテーブル引きに。 // 2010/09/27 一旦、精度上の問題で戻しました。 storagePtr[index].x = nw::math::AcosRad(ySrcAxis.y); // storagePtr[index].x = internal::AcosTableRad(ySrcAxis.y); // nw::math::Atan2Rad よりも std::atan2fの方が速い(SDK0.13) storagePtr[index].y = ::std::atan2f(ySrcAxis.x, ySrcAxis.z); storagePtr[index].z = 0; } } } break; } NW_PARTICLE_PROFILE_STOP(); } void ParticleTexturePatternUpdater( const ResParticleUpdaterData* updater, ParticleCollection* collection, ParticleTime time, int updaterIndex ) { NW_PARTICLE_PROFILE_START("ParticleTexturePatternUpdater"); ParticleTime* birth = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_BIRTH, PARTICLE_BUFFER_FRONT); ParticleTime* life; ParticleTime lifeParam = 1.0f; collection->GetStreamOrParameter(PARTICLEUSAGE_LIFE, &life, &lifeParam, PARTICLE_BUFFER_FRONT); int animationLength = 0; //int animationDimension = 0; int animationStride = 0; bool* animationEnable = NULL; f32* animationData = NULL; ResParticleTexturePatternUpdater texUpdater(updater); NW_ASSERT(texUpdater.IsValid()); ResParticleAnimation animation = texUpdater.GetParticleAnimation(); if (animation.IsValid()) { animationLength = animation.GetAnimationLength(); //animationDimension = animation.GetAnimationDimension(); animationStride = animation.GetAnimationStride(); animationEnable = animation.GetAnimationEnabled(); animationData = animation.GetAnimationData(); } ParticleUsage target = texUpdater.GetTargetStream(); nw::math::VEC2* storagePtr = (nw::math::VEC2*)collection->GetStreamPtr(target, PARTICLE_BUFFER_FRONT); NW_NULL_ASSERT(storagePtr); const int divisionX = (int)texUpdater.GetDivisionX(); const int divisionY = (int)texUpdater.GetDivisionY(); const f32 halfDivisionX = (int)texUpdater.GetDivisionX() * 0.5f; const f32 halfDivisionY = (int)texUpdater.GetDivisionY() * 0.5f; const int textureIndexMax = divisionX * divisionY - 1; const u32 maxIndex = collection->GetMaxActiveIndex() + 1; for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index) { int value = texUpdater.GetTextureIndex(); if (animationData != NULL) { ResParticleAnimationOption animationOption = animation.GetParticleAnimationOption(); ParticleTime ptclTime = animationOption.EvaluateAnimationFrame( index, birth[index], (life == NULL) ? lifeParam : life[index], time, updaterIndex); #ifdef NW_GFX_PARTICLE_COMPAT_1_1 if (ptclTime < 0) { ptclTime = 0; } if (ptclTime >= animationLength) { ptclTime = (f32)animationLength - 1; } int animationDataPos = (int)ptclTime * animationStride; #else if (ptclTime.GetParticleTimeValue() < 0) { ptclTime = 0; } if (ptclTime >= ParticleTime(animationLength)) { ptclTime = animationLength - 1; } int animationDataPos = ptclTime.GetS32Value() * animationStride; #endif if (animationEnable[0]) { value = (int)animationData[animationDataPos++]; } if (value < 0) { value = 0; } if (value > textureIndexMax) { value = textureIndexMax; } } storagePtr[index].x = -0.5f + (halfDivisionX) - (value % divisionX); storagePtr[index].y = 0.5f - (halfDivisionY) + (divisionY - (value / divisionX) - 1); //storagePtr[index].y = -0.5f + (halfDivisionY) - (divisionY - (value / divisionX) - 1); } NW_PARTICLE_PROFILE_STOP(); } void ParticleChildUpdater( const ResParticleUpdaterData* updater, ParticleCollection* collection, u16* activeIndex, int startIndex, int incrIndex, int count, ParticleTime time, ParticleTime prevTime, u32 work, ParticleSet* parent, #ifdef NW_GFX_PARTICLE_COMPAT_1_1 ParticleContext* particleContext, #endif ParticleRandom* random ) { NW_PARTICLE_PROFILE_START("ParticleChildUpdater"); ParticleTime* birth = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_BIRTH, PARTICLE_BUFFER_FRONT); ParticleTime* life; ParticleTime lifeParam = 1.0f; collection->GetStreamOrParameter(PARTICLEUSAGE_LIFE, &life, &lifeParam, PARTICLE_BUFFER_FRONT); ResParticleChildUpdater childUpdater(updater); NW_ASSERT(childUpdater.IsValid()); const ResParticleForm resForm = childUpdater.GetParticleForm(); ParticleSet* particleSet = reinterpret_cast(work); if (particleSet != NULL && resForm.IsValid()) { const nw::math::MTX34 modelMtx = parent->WorldMatrix(); nw::math::VEC3* translate = (nw::math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_TRANSLATE, PARTICLE_BUFFER_FRONT); #if 0 nw::math::VEC3* rotate; nw::math::VEC3 rotateParam; collection->GetStreamOrParameter(PARTICLEUSAGE_ROTATE, &rotate, &rotateParam, PARTICLE_BUFFER_FRONT); nw::math::VEC3* scale; nw::math::VEC3 scaleParam; collection->GetStreamOrParameter(PARTICLEUSAGE_SCALE, &scale, &scaleParam, PARTICLE_BUFFER_FRONT); #endif #ifdef NW_GFX_PARTICLE_COMPAT_1_1 int newParticleCount = 0; nw::math::VEC3* positionsHead = particleContext->GetEmissionPositionWork(); u16* parentsHead = particleContext->GetEmissionParentWork(); #endif int emissionCount = childUpdater.GetEmissionRatio(); ResParticleChildUpdaterOption updaterOption = childUpdater.GetTiming(); #ifdef NW_GFX_PARTICLE_COMPAT_1_1 ParticleTime* limit = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_NEG_TIMELIMIT, PARTICLE_BUFFER_FRONT); #endif #ifdef NW_GFX_PARTICLE_COMPAT_1_1 #else // 格納先のポインタ等を集める ParticleCollection* targetCollection = particleSet->GetParticleCollection(); bool targetIsAscendingOrder = particleSet->IsAscendingOrder(); const int targetIncrIndex = targetIsAscendingOrder ? 1 : -1; nw::math::VEC3* targetTranslate = (nw::math::VEC3*)targetCollection->GetStreamPtr(PARTICLEUSAGE_TRANSLATE, PARTICLE_BUFFER_FRONT); NW_NULL_ASSERT(targetTranslate); bool useTransform = false; nw::math::VEC3* targetVelocity = NULL; nw::math::MTX34 transformMatrix; if (particleSet->WorldMatrix() != modelMtx) { useTransform = true; targetVelocity = (nw::math::VEC3*)targetCollection->GetStreamPtr(PARTICLEUSAGE_VELOCITY, PARTICLE_BUFFER_FRONT); nw::math::MTX34Mult(&transformMatrix, particleSet->InverseWorldMatrix(), modelMtx); } u16* targetActiveIndex = (u16*)targetCollection->GetStreamPtr(PARTICLEUSAGE_ACTIVEINDEX, PARTICLE_BUFFER_FRONT); NW_NULL_ASSERT(targetActiveIndex); int targetStartIndex = targetIsAscendingOrder ? targetCollection->GetCount() : targetCollection->GetCapacity() - targetCollection->GetCount() - 1; targetActiveIndex += targetStartIndex; #endif u16* pActiveIndex = activeIndex + startIndex; for (int j = 0; j < count; ++j) { int index = *pActiveIndex; pActiveIndex += incrIndex; if (updaterOption.CheckTiming( birth[index], (life == NULL) ? lifeParam : life[index], #ifdef NW_GFX_PARTICLE_COMPAT_1_1 limit[index], #endif prevTime, time)) { #ifdef NW_GFX_PARTICLE_COMPAT_1_1 nw::math::VEC3* newPositions = &positionsHead[newParticleCount]; if (emissionCount > particleContext->GetEmissionWorkCapacity() - newParticleCount) { emissionCount = particleContext->GetEmissionWorkCapacity() - newParticleCount; } for (int k = 0; k < emissionCount; ++k) { parentsHead[newParticleCount + k] = index; } #else emissionCount = particleSet->AddParticles(emissionCount); if (emissionCount == 0) { break; } #endif const ResParticleCubeForm cubeForm = ResDynamicCast(resForm); if (cubeForm.IsValid()) { #ifdef NW_GFX_PARTICLE_COMPAT_1_1 ParticleEmitter::CalcCubeForm(cubeForm, emissionCount, random, newPositions); #else ParticleEmitter::CalcCubeForm(cubeForm, emissionCount, random, targetActiveIndex, targetIncrIndex, targetTranslate); #endif } const ResParticleCylinderForm cylinderForm = ResDynamicCast(resForm); if (cylinderForm.IsValid()) { #ifdef NW_GFX_PARTICLE_COMPAT_1_1 ParticleEmitter::CalcCylinderForm(cylinderForm, emissionCount, random, newPositions); #else ParticleEmitter::CalcCylinderForm(cylinderForm, emissionCount, random, targetActiveIndex, targetIncrIndex, targetTranslate); #endif } const ResParticleDiscForm discForm = ResDynamicCast(resForm); if (discForm.IsValid()) { #ifdef NW_GFX_PARTICLE_COMPAT_1_1 ParticleEmitter::CalcDiscForm(discForm, emissionCount, random, newPositions); #else ParticleEmitter::CalcDiscForm(discForm, emissionCount, random, targetActiveIndex, targetIncrIndex, targetTranslate); #endif } const ResParticlePointForm pointForm = ResDynamicCast(resForm); if (pointForm.IsValid()) { #ifdef NW_GFX_PARTICLE_COMPAT_1_1 ParticleEmitter::CalcPointForm(pointForm, emissionCount, random, newPositions); #else ParticleEmitter::CalcPointForm(pointForm, emissionCount, random, targetActiveIndex, targetIncrIndex, targetTranslate); #endif } const ResParticleSphereForm sphereForm = ResDynamicCast(resForm); if (sphereForm.IsValid()) { #ifdef NW_GFX_PARTICLE_COMPAT_1_1 ParticleEmitter::CalcSphereForm(sphereForm, emissionCount, random, newPositions); #else ParticleEmitter::CalcSphereForm(sphereForm, emissionCount, random, targetActiveIndex, targetIncrIndex, targetTranslate); #endif } const ResParticleRectangleForm rectangleForm = ResDynamicCast(resForm); if (rectangleForm.IsValid()) { #ifdef NW_GFX_PARTICLE_COMPAT_1_1 ParticleEmitter::CalcRectangleForm(rectangleForm, emissionCount, random, newPositions); #else ParticleEmitter::CalcRectangleForm(rectangleForm, emissionCount, random, targetActiveIndex, targetIncrIndex, targetTranslate); #endif } #ifdef NW_GFX_PARTICLE_COMPAT_1_1 newParticleCount += emissionCount; #else { ParticleModel* model = static_cast(particleSet->GetParent()); NW_NULL_ASSERT(model); ParticleTime targetTime = model->ParticleAnimFrameController().GetFrame(); particleSet->InitializeParticles(targetStartIndex, emissionCount, targetIncrIndex, targetTime); targetStartIndex += targetIncrIndex * emissionCount; } if (useTransform) { nw::math::MTX34 workMatrix; nw::math::MTX34 particleMatrix; nw::math::MTX34Identity(&particleMatrix); #if 0 nw::math::MTX34Translate(&particleMatrix, translate[index]); ////nw::math::MTX34RotXYZRad(&workMatrix, rotate[index].x, rotate[index].y, rotate[index].z); ////nw::math::MTX34Mult(&particleMatrix, particleMatrix, workMatrix); ////nw::math::MTX34Scale(&workMatrix, scale[index]); ////nw::math::MTX34Mult(&particleMatrix, particleMatrix, workMatrix); nw::math::MTX34Mult(&workMatrix, transformMatrix, particleMatrix); for (int i = 0; i < emissionCount; ++i) { int dstIndex = *targetActiveIndex; targetActiveIndex += targetIncrIndex; nw::math::VEC3Transform(&targetTranslate[dstIndex], workMatrix, targetTranslate[dstIndex]); nw::math::VEC3TransformNormal(&targetVelocity[dstIndex], workMatrix, targetVelocity[dstIndex]); } #else for (int i = 0; i < emissionCount; ++i) { int dstIndex = *targetActiveIndex; targetActiveIndex += targetIncrIndex; targetTranslate[dstIndex] += translate[index]; nw::math::VEC3Transform(&targetTranslate[dstIndex], transformMatrix, targetTranslate[dstIndex]); nw::math::VEC3TransformNormal(&targetVelocity[dstIndex], transformMatrix, targetVelocity[dstIndex]); } #endif } else { for (int i = 0; i < emissionCount; ++i) { int dstIndex = *targetActiveIndex; targetActiveIndex += targetIncrIndex; targetTranslate[dstIndex] += translate[index]; } } #endif } } #ifdef NW_GFX_PARTICLE_COMPAT_1_1 if (newParticleCount > 0) { particleSet->AddParticles( modelMtx, positionsHead, parent, parentsHead, newParticleCount); } #endif } NW_PARTICLE_PROFILE_STOP(); } //---------------------------------------- void ParticleSet::UpdateParticles(ParticleContext* particleContext) { NW_ASSERT(ut::IsTypeOf(this->GetParent())); ParticleModel* model = static_cast(this->GetParent()); NW_NULL_ASSERT(model); ParticleCollection* collection = this->GetParticleCollection(); collection->SwapBuffer(); const ParticleTime prevTime = model->ParticleAnimFrameController().GetAnimFrame().GetLastFrame(); const ParticleTime time = model->ParticleAnimFrameController().GetFrame(); #ifdef NW_GFX_PARTICLE_COMPAT_1_1 const f32 diffTime = time - prevTime; if (diffTime < 0) { return; } #else const ParticleTime diffTimePtcl = time - prevTime; if (diffTimePtcl < 0) { return; } const f32 diffTime = diffTimePtcl.GetFloat32Value(); #endif const int collectionCapacity = collection->GetCapacity(); const int count = collection->GetCount(); if (count == 0) { return; } if (!m_Updaters.empty()) { int updaterIndex = 0; ut::MoveArray::iterator endIter = this->m_Updaters.end(); for (ut::MoveArray::iterator iter = this->m_Updaters.begin(); iter != endIter; ++updaterIndex) { Updater& workUpdater = *iter++; if (workUpdater.resource == NULL) { continue; } const ResParticleUpdater updater(workUpdater.resource); if (!updater.GetUpdaterEnabled()) { continue; } void* targetStream; if (collection->GetBufferSide()) { targetStream = workUpdater.m_TargetStreams[1]; } else { targetStream = workUpdater.m_TargetStreams[0]; } switch (workUpdater.m_Type) { case ResParticleAccelarationUpdater::TYPE_INFO: ParticleAccelarationUpdater(updater.ptr(), collection, diffTime); break; case ResParticleFloatImmediateUpdater::TYPE_INFO: ParticleFloatImmediateUpdater(updater.ptr(), collection, time, updaterIndex); break; #ifdef NW_GFX_PARTICLE_4KEY case ResParticleFloatImmediate4KeyUpdater::TYPE_INFO: ParticleFloatImmediate4KeyUpdater(updater.ptr(), collection, time); break; #endif case ResParticleGravityUpdater::TYPE_INFO: ParticleGravityUpdater((nw::math::VEC3*)targetStream, updater.ptr(), collection, diffTime); break; case ResParticleSpinUpdater::TYPE_INFO: ParticleSpinUpdater(updater.ptr(), collection, diffTime); break; case ResParticleRandomUpdater::TYPE_INFO: ParticleRandomUpdater(updater.ptr(), collection, time, prevTime, this->m_ParticleRandom); break; case ResParticleVector2ImmediateUpdater::TYPE_INFO: ParticleVector2ImmediateUpdater(updater.ptr(), collection, time, updaterIndex); break; case ResParticleVector3AdditiveUpdater::TYPE_INFO: { NW_PARTICLE_PROFILE_START("ParticleVector3AdditiveUpdater"); if (workUpdater.IsEnabledFlags(Updater::FLAG_IS_HAS_CURVE_ANIM)) { ParticleVector3AdditiveUpdater(updater.ptr(), collection, time, diffTime, updaterIndex); } else { ResParticleVector3AdditiveUpdater vec3Updater(updater.ptr()); NW_ASSERT(vec3Updater.IsValid()); nw::math::VEC3* storagePtr = (nw::math::VEC3*)targetStream; NW_NULL_ASSERT(storagePtr); nw::math::VEC3 defaultValue = vec3Updater.GetDefaultValue(); defaultValue *= diffTime; storagePtr += collection->GetMinActiveIndex(); const int processCount = collection->GetMaxActiveIndex() - collection->GetMinActiveIndex() + 1; internal::VEC3ArrayAddScalar(storagePtr, &defaultValue, processCount); } NW_PARTICLE_PROFILE_STOP(); } break; case ResParticleVector3ImmediateUpdater::TYPE_INFO: { NW_PARTICLE_PROFILE_START("ParticleVector3ImmediateUpdater"); if (workUpdater.IsEnabledFlags(Updater::FLAG_IS_HAS_CURVE_ANIM)) { ParticleVector3ImmediateUpdater(updater.ptr(), collection, time, updaterIndex); } else { ResParticleVector3ImmediateUpdater vec3Updater(updater.ptr()); NW_ASSERT(vec3Updater.IsValid()); const nw::math::VEC3 defaultValue = vec3Updater.GetDefaultValue(); nw::math::VEC3* storagePtr = (nw::math::VEC3*)targetStream; NW_NULL_ASSERT(storagePtr); const u32 maxIndex = collection->GetMaxActiveIndex() + 1; for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index) { storagePtr[index] = defaultValue; } } NW_PARTICLE_PROFILE_STOP(); } break; case ResParticleVector3RandomAdditiveUpdater::TYPE_INFO: { NW_PARTICLE_PROFILE_START("ParticleVector3RandomAdditiveUpdater"); if (workUpdater.IsEnabledFlags(Updater::FLAG_IS_HAS_CURVE_ANIM)) { ParticleVector3RandomAdditiveUpdater(updater.ptr(), collection, time, diffTime, updaterIndex); } else { ResParticleVector3RandomAdditiveUpdater vec3Updater(updater.ptr()); NW_ASSERT(vec3Updater.IsValid()); const nw::math::VEC3 defaultValue = vec3Updater.GetDefaultValue(); nw::math::VEC3* storagePtr = (nw::math::VEC3*)targetStream; NW_NULL_ASSERT(storagePtr); const u32 maxIndex = collection->GetMaxActiveIndex() + 1; for (u32 index = collection->GetMinActiveIndex(); index < maxIndex; ++index) { storagePtr[index] = defaultValue * diffTime; } } NW_PARTICLE_PROFILE_STOP(); } break; case ResParticleRotateUpVectorUpdater::TYPE_INFO: ParticleRotateUpVectorUpdater(updater.ptr(), collection); break; case ResParticleTexturePatternUpdater::TYPE_INFO: ParticleTexturePatternUpdater(updater.ptr(), collection, time, updaterIndex); break; case ResParticleChildUpdater::TYPE_INFO: { u16* activeIndex = (u16*)collection->GetStreamPtr(PARTICLEUSAGE_ACTIVEINDEX, PARTICLE_BUFFER_BACK); const bool isAscendingOrder = this->IsAscendingOrder(); const int startIndex = (isAscendingOrder) ? 0 : collectionCapacity - 1; const int incrIndex = (isAscendingOrder) ? 1 : -1; #ifdef NW_GFX_PARTICLE_COMPAT_1_1 ParticleChildUpdater(updater.ptr(), collection, activeIndex, startIndex, incrIndex, count, time, prevTime, workUpdater.work, this, particleContext, &this->m_ParticleRandom); #else ParticleChildUpdater(updater.ptr(), collection, activeIndex, startIndex, incrIndex, count, time, prevTime, workUpdater.work, this, &this->m_ParticleRandom); #endif } break; case ResParticleUserUpdater::TYPE_INFO: { NW_PARTICLE_PROFILE_START("ParticleUserUpdater"); ResParticleUserUpdater userUpdater(updater.ptr()); NW_ASSERT(userUpdater.IsValid()); if (workUpdater.work != 0) { (*(ResParticleUserUpdater::UserFunctionType)workUpdater.work)( particleContext, this, // ParticleSet* &userUpdater, #ifdef NW_GFX_PARTICLE_COMPAT_1_1 prevTime, time #else prevTime.GetFloat32Value(), time.GetFloat32Value() #endif ); } NW_PARTICLE_PROFILE_STOP(); } break; case ResParticleGeneralUpdater::TYPE_INFO: // 位置の更新 { NW_PARTICLE_PROFILE_START("ParticleGeneralUpdater"); math::VEC3* trans = (math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_TRANSLATE, PARTICLE_BUFFER_FRONT); math::VEC3* velocity = (math::VEC3*)collection->GetStreamPtr(PARTICLEUSAGE_VELOCITY, PARTICLE_BUFFER_FRONT); trans += collection->GetMinActiveIndex(); velocity += collection->GetMinActiveIndex(); const int processCount = collection->GetMaxActiveIndex() - collection->GetMinActiveIndex() + 1; internal::VEC3ArrayAddVEC3Array(trans, velocity, diffTime, processCount); NW_PARTICLE_PROFILE_STOP(); } break; } } } // 消滅処理 { #ifdef NW_GFX_PARTICLE_COMPAT_1_1 u32* work = reinterpret_cast(particleContext->GetEmissionPositionWork()); // TBD int minActiveIndex = collection->GetMinActiveIndex(); int maxActiveIndex = collection->GetMaxActiveIndex(); if (minActiveIndex <= maxActiveIndex) { NW_ASSERT(particleContext->GetEmissionWorkCapacity() * 3 >= minActiveIndex + ut::RoundUp(maxActiveIndex - minActiveIndex + 1, 8)); f32* limit = (f32*)collection->GetStreamPtr(PARTICLEUSAGE_NEG_TIMELIMIT, PARTICLE_BUFFER_FRONT); internal::CalcRemainFrame( reinterpret_cast(&work[minActiveIndex]), &limit[minActiveIndex], time, maxActiveIndex - minActiveIndex + 1); } int aliveCount = 0; int destroyCount = 0; const int prevCollectionCount = collection->GetCount(); const bool isAscendingOrder = this->IsAscendingOrder(); const int startIndex = (isAscendingOrder) ? 0 : collectionCapacity - 1; const int incrIndex = (isAscendingOrder) ? 1 : -1; u16* activeIndex = (u16*)collection->GetStreamPtr(PARTICLEUSAGE_ACTIVEINDEX, PARTICLE_BUFFER_BACK); u16* newActiveIndex = (u16*)collection->GetStreamPtr(PARTICLEUSAGE_ACTIVEINDEX, PARTICLE_BUFFER_FRONT); u16* freeIndex = (u16*)collection->GetStreamPtr(PARTICLEUSAGE_FREEINDEX, PARTICLE_BUFFER_FRONT); minActiveIndex = 0xffff; maxActiveIndex = 0; { u16* pActiveIndex = activeIndex + startIndex; u16* pAliveIndex = newActiveIndex + startIndex; u16* pDestroyIndex = freeIndex + collectionCapacity - prevCollectionCount; for (int i = prevCollectionCount; i != 0; --i) { int index = *pActiveIndex; pActiveIndex += incrIndex; NW_ASSERT(index != 0xffff); if ((work[index] & 0x80000000) == 0) { *pDestroyIndex++ = index; ++destroyCount; } else { if (minActiveIndex > index) { minActiveIndex = index; } if (maxActiveIndex < index) { maxActiveIndex = index; } *pAliveIndex = index; // 別バッファにコピーしつつ整列していく pAliveIndex += incrIndex; ++aliveCount; } } } #else ParticleTime* birth = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_BIRTH, PARTICLE_BUFFER_FRONT); int aliveCount = 0; const int prevCollectionCount = collection->GetCount(); const bool isAscendingOrder = this->IsAscendingOrder(); const int startIndex = (isAscendingOrder) ? 0 : collectionCapacity - 1; const int incrIndex = (isAscendingOrder) ? 1 : -1; u16* pActiveIndex = (u16*)collection->GetStreamPtr(PARTICLEUSAGE_ACTIVEINDEX, PARTICLE_BUFFER_BACK); pActiveIndex += startIndex; u16* pAliveIndex = (u16*)collection->GetStreamPtr(PARTICLEUSAGE_ACTIVEINDEX, PARTICLE_BUFFER_FRONT); pAliveIndex += startIndex; u16* pDestroyIndex = (u16*)collection->GetStreamPtr(PARTICLEUSAGE_FREEINDEX, PARTICLE_BUFFER_FRONT); pDestroyIndex += collectionCapacity - prevCollectionCount; int minActiveIndex = 0xffff; int maxActiveIndex = 0; if (collection->IsStream(PARTICLEUSAGE_LIFE)) { ParticleTime* life = (ParticleTime*)collection->GetStreamPtr(PARTICLEUSAGE_LIFE, PARTICLE_BUFFER_FRONT); for (int i = prevCollectionCount; i != 0; --i) { int index = *pActiveIndex; pActiveIndex += incrIndex; NW_ASSERT(index != 0xffff); if (birth[index].GetParticleTimeValue() + life[index].GetParticleTimeValue() <= time.GetParticleTimeValue()) { *pDestroyIndex++ = index; } else { if (minActiveIndex > index) { minActiveIndex = index; } if (maxActiveIndex < index) { maxActiveIndex = index; } *pAliveIndex = index; // 別バッファにコピーしつつ整列していく pAliveIndex += incrIndex; ++aliveCount; } } } else { ParticleTime* lifeParam = (ParticleTime*)collection->GetParameterPtr(PARTICLEUSAGE_LIFE); ParticleTime limit = time - *lifeParam; for (int i = prevCollectionCount; i != 0; --i) { int index = *pActiveIndex; pActiveIndex += incrIndex; NW_ASSERT(index != 0xffff); if (birth[index].GetParticleTimeValue() <= limit.GetParticleTimeValue()) { *pDestroyIndex++ = index; } else { if (minActiveIndex > index) { minActiveIndex = index; } if (maxActiveIndex < index) { maxActiveIndex = index; } *pAliveIndex = index; // 別バッファにコピーしつつ整列していく pAliveIndex += incrIndex; ++aliveCount; } } } #endif collection->SetMinActiveIndex(minActiveIndex); collection->SetMaxActiveIndex(maxActiveIndex); collection->SetCount(aliveCount); } } //---------------------------------------- ParticleSet::ParticleSet( os::IAllocator* allocator, ResParticleSet resObj, const ParticleSet::Description& description) : SceneNode(allocator, resObj, description), m_ParticleCollection(NULL), m_ScaleOffset(1.0f, 1.0f, 1.0f), m_RotateOffset(0, 0, 0) { } //---------------------------------------- ParticleSet::~ParticleSet() { { ut::MoveArray::iterator endIter = this->m_Initializers.end(); for (ut::MoveArray::iterator iter = this->m_Initializers.begin(); iter != endIter; ++iter) { if ((*iter).m_IsCopied) { ResParticleInitializerData* ptr = const_cast((*iter).resource); this->GetAllocator().Free(ptr); (*iter).resource = NULL; } } } { ut::MoveArray::iterator endIter = this->m_Updaters.end(); for (ut::MoveArray::iterator iter = this->m_Updaters.begin(); iter != endIter; ++iter) { if ((*iter).m_IsCopied) { ResParticleUpdaterData* ptr = const_cast((*iter).resource); this->GetAllocator().Free(ptr); (*iter).resource = NULL; } } } SafeDestroy(m_ParticleCollection); } //---------------------------------------- void ParticleSet::Accept( ISceneVisitor* visitor ) { visitor->VisitParticleSet(this); AcceptChildren(visitor); } //---------------------------------------- void ParticleSet::ClearParticleCollection() { if (this->m_ParticleCollection == NULL) { return; } this->m_ParticleCollection->Clear(); } //---------------------------------------- bool ResParticleChildUpdaterOption::CheckTiming( ParticleTime birth, ParticleTime life, #ifdef NW_GFX_PARTICLE_COMPAT_1_1 ParticleTime limit, #endif ParticleTime prevTime, ParticleTime currentTime) { switch (this->GetTypeInfo()) { case ResParticleChildUpdaterFirstUpdateOption::TYPE_INFO: return birth == currentTime; case ResParticleChildUpdaterFinalUpdateOption::TYPE_INFO: #ifdef NW_GFX_PARTICLE_COMPAT_1_1 return limit + currentTime >= 0; #else return birth.GetParticleTimeValue() + life.GetParticleTimeValue() <= currentTime.GetParticleTimeValue(); #endif case ResParticleChildUpdaterIntervalOption::TYPE_INFO: { ResParticleChildUpdaterIntervalOption resource(this->ptr()); if (prevTime >= currentTime) { return false; } #ifdef NW_GFX_PARTICLE_COMPAT_1_1 int end = (int)(resource.GetEnd() * life); int prevCount =(int)(prevTime - birth); #else int end = (int)(resource.GetEnd() * life.GetFloat32Value()); ParticleTime prev_birth = prevTime - birth; int prevCount = prev_birth.GetIntegralParts(); #endif if (prevCount >= end) { return false; } int interval = resource.GetInterval(); if (interval < 1) { interval = 1; } #ifdef NW_GFX_PARTICLE_COMPAT_1_1 int start = (int)(resource.GetStart() * life); prevCount = (int)math::FFloor((float)(prevCount - start) / interval); int currentCount = (int)math::FFloor((currentTime - birth - start) / interval); #else int start = (int)(resource.GetStart() * life.GetFloat32Value()); prevCount = (int)math::FFloor((float)(prevCount - start) / interval); ParticleTime current_birth =currentTime - birth; current_birth -= start; int currentCount = (int)math::FFloor(current_birth.GetFloat32Value() / interval); #endif if (currentCount < 0) { return false; } return prevCount < currentCount; } case ResParticleChildUpdaterFrameOption::TYPE_INFO: { ResParticleChildUpdaterFrameOption resource(this->ptr()); if (prevTime >= currentTime) { return false; } #ifdef NW_GFX_PARTICLE_COMPAT_1_1 int prevCount =(int)(prevTime - birth); #else ParticleTime prev_birth = prevTime - birth; int prevCount = prev_birth.GetIntegralParts(); #endif if (prevCount >= resource.GetEnd()) { return false; } int interval = resource.GetInterval(); if (interval < 1) { interval = 1; } prevCount = (int)math::FFloor((float)(prevCount - resource.GetStart()) / interval); #ifdef NW_GFX_PARTICLE_COMPAT_1_1 int currentCount = (int)math::FFloor((currentTime - birth - resource.GetStart()) / interval); #else ParticleTime current_birth =currentTime - birth; current_birth -= resource.GetStart(); int currentCount = (int)math::FFloor(current_birth.GetFloat32Value() / interval); #endif if (currentCount < 0) { return false; } return prevCount < currentCount; } } return false; } } // namespace gfx } // namespace nw