1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     gfx_Fog.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: 29649 $
14  *---------------------------------------------------------------------------*/
15 
16 #include "precompiled.h"
17 
18 #include <nw/os/os_Memory.h>
19 
20 #include <nw/gfx/gfx_Fog.h>
21 #include <nw/gfx/gfx_ISceneVisitor.h>
22 #include <nw/gfx/gfx_Camera.h>
23 
24 namespace nw
25 {
26 namespace gfx
27 {
28 
29 NW_UT_RUNTIME_TYPEINFO_DEFINITION(Fog, TransformNode);
30 const float Fog::FOG_DENSITY = 0.0f;
31 const ResFogUpdater::FogUpdaterType Fog::FOG_UPDATER_TYPE = ResFogUpdater::FOG_UPDATER_TYPE_NONE;
32 const float Fog::FOG_MAX_DEPTH = 0.0f;
33 const float Fog::FOG_MIN_DEPTH = 0.0f;
34 
35 //----------------------------------------
36 Fog*
Create(os::IAllocator * allocator)37 Fog::DynamicBuilder::Create(
38     os::IAllocator* allocator
39 )
40 {
41     NW_NULL_ASSERT(allocator);
42 
43     ResPtr resource(
44         CreateResFog(allocator),
45         ResFogDataDestroyer(allocator));
46 
47     void* memory = allocator->Alloc(sizeof(Fog));
48     NW_NULL_ASSERT(memory);
49     Fog* fog = new(memory) Fog(
50         allocator,
51         resource,
52         m_Description);
53     Result result = fog->Initialize(allocator);
54     NW_ASSERT(result.IsSuccess());
55 
56     return fog;
57 }
58 
59 //----------------------------------------------------------
60 size_t
GetMemorySize(size_t alignment) const61 Fog::DynamicBuilder::GetMemorySize( size_t alignment ) const
62 {
63     NW_ASSERT(this->m_Description.isFixedSizeMemory);
64 
65     os::MemorySizeCalculator size(alignment);
66 
67     // Fog::CreateResFox
68     size += sizeof(ResFogData);
69     size += sizeof(ResImageLookupTableData);
70     size += sizeof(ResFogUpdaterData);
71     size += sizeof(u32) * NW_FOG_TABLE_COMMAND_NUM;
72 
73     size += sizeof(Fog);
74 
75     // Fog::Initialize
76     TransformNode::GetMemorySizeForInitialize(
77         &size,
78         ResTransformNode(),
79         m_Description);
80 
81     // Fog::CreateResFogUpdater
82     size += sizeof(ResFogUpdaterData);
83 
84     // Fog::CreateAnimGroup
85     // DynamicBuilder 使用時には何も行なわれない。
86 
87     // Fog::CreateOriginalValue
88     size += sizeof(ResFogData);
89 
90     return size.GetSizeWithPadding(alignment);
91 }
92 
93 //----------------------------------------
94 void
Accept(ISceneVisitor * visitor)95 Fog::Accept(
96     ISceneVisitor* visitor
97 )
98 {
99     visitor->VisitFog(this);
100     AcceptChildren(visitor);
101 }
102 
103 //----------------------------------------
104 /* static */ Fog*
Create(SceneNode * parent,ResSceneObject resource,const Fog::Description & description,os::IAllocator * allocator)105 Fog::Create(
106     SceneNode* parent,
107     ResSceneObject resource,
108     const Fog::Description& description,
109     os::IAllocator* allocator
110 )
111 {
112     NW_NULL_ASSERT(allocator);
113 
114     ResFog resNode = ResDynamicCast<ResFog>(resource);
115     NW_ASSERT(resNode.IsValid());
116     NW_ASSERT( internal::ResCheckRevision( resNode ) );
117 
118     void* memory = allocator->Alloc(sizeof(Fog));
119     NW_NULL_ASSERT(memory);
120 
121     Fog* fog = new(memory) Fog(
122         allocator,
123         resNode,
124         description);
125     Result result = fog->Initialize(allocator);
126     NW_ASSERT(result.IsSuccess());
127 
128     if (parent)
129     {
130         bool isAttached = parent->AttachChild(fog);
131         NW_ASSERT(isAttached);
132     }
133 
134     return fog;
135 }
136 
137 //-----------------------------------------
138 /* static*/ ResFogData*
CreateResFog(os::IAllocator * allocator,const char * name)139 Fog::CreateResFog(os::IAllocator* allocator, const char* name /* = NULL */)
140 {
141     // TODO: 細かくアロケートする実装になっているが、まとめて確保できる仕組みも追加予定。
142 
143     ResFogData* resFog =
144         AllocateAndFillN<ResFogData>(allocator, sizeof(ResFogData), 0);
145 
146     //--------------------------------
147     // ResSceneObjectData のメンバ初期化
148     resFog->typeInfo = ResFog::TYPE_INFO;
149     resFog->m_Header.revision = ResFog::BINARY_REVISION;
150     resFog->m_Header.signature = ResFog::SIGNATURE;
151 
152     resFog->m_UserDataDicCount = 0;
153     resFog->toUserDataDic.set_ptr( NULL );
154 
155     resFog->toName.set_ptr(AllocateAndCopyString(name, allocator, MAX_NAME_LENGTH));
156 
157     //--------------------------------
158     // ResSceneNodeData のメンバ初期化
159     resFog->m_ChildrenTableCount = 0;
160     resFog->toChildrenTable.set_ptr( NULL );
161     resFog->m_AnimGroupsDicCount = NULL;
162     resFog->toAnimGroupsDic.set_ptr( NULL );
163 
164     //--------------------------------
165     // ResTransformNode のメンバ初期化
166     const math::VEC3 scale(1.0f, 1.0f, 1.0f);
167     const math::VEC3 rotate(0.0f, 0.0f, 0.0f);
168     const math::VEC3 translate(0.0f, 0.0f, 0.0f);
169     resFog->m_Transform = math::Transform3(scale, rotate, translate);
170     resFog->m_WorldMatrix = math::MTX34::Identity();
171     ResTransformNode(resFog).SetBranchVisible(true);
172 
173     //--------------------------------
174     // ResFogData のメンバ初期化
175     ResImageLookupTableData* fogSampler =
176         AllocateAndFill<ResImageLookupTableData>(allocator, 0);
177 
178     ResFogUpdaterData* fogUpdater =
179         AllocateAndFill<ResFogUpdaterData>(allocator, 0);
180 
181     resFog->toFogUpdater.set_ptr( fogUpdater );
182     resFog->toFogSampler.set_ptr( fogSampler );
183 
184     //--------------------------------
185     // ResImageLookupTableData のメンバ初期化
186     fogSampler->typeInfo = ResImageLookupTable::TYPE_INFO;
187 
188     void* tableMemory = allocator->AllocAndFill<u32>(NW_FOG_TABLE_COMMAND_NUM, 0);
189     fogSampler->m_CommandCacheTableCount = NW_FOG_TABLE_COMMAND_SIZE;
190     fogSampler->toCommandCacheTable.set_ptr( tableMemory );
191 
192     return resFog;
193 }
194 
195 //-----------------------------------------
196 /* static */ void
DestroyResFog(os::IAllocator * allocator,ResFogData * resFog)197 Fog::DestroyResFog(os::IAllocator* allocator, ResFogData* resFog)
198 {
199     NW_NULL_ASSERT( allocator );
200     NW_NULL_ASSERT( resFog );
201 
202     if ( resFog->toName.to_ptr() != NULL )
203     {
204         allocator->Free( const_cast<char*>( resFog->toName.to_ptr() ) );
205     }
206 
207     if (resFog->toFogUpdater.to_ptr() != NULL)
208     {
209         allocator->Free( resFog->toFogUpdater.to_ptr() );
210     }
211 
212     if (resFog->toFogSampler.to_ptr() != NULL)
213     {
214         ResImageLookupTable fogSampler = ResImageLookupTable(resFog->toFogSampler.to_ptr());
215         if (fogSampler.ref().toCommandCacheTable.to_ptr() != NULL)
216         {
217             allocator->Free( fogSampler.ref().toCommandCacheTable.to_ptr() );
218         }
219 
220         allocator->Free( resFog->toFogSampler.to_ptr() );
221     }
222 
223     allocator->Free( resFog );
224 }
225 //-----------------------------------------
226 /* static*/ ResFogUpdaterData*
CreateResFogUpdater(os::IAllocator * allocator)227 Fog::CreateResFogUpdater(os::IAllocator* allocator)
228 {
229     ResFogUpdaterData* fogUpdater =
230         AllocateAndFill<ResFogUpdaterData>(allocator, 0);
231 
232     NW_NULL_ASSERT(fogUpdater);
233 
234     return fogUpdater;
235 }
236 
237 //-----------------------------------------
238 /* static */ void
DestroyResFogUpdater(os::IAllocator * allocator,ResFogUpdaterData * resFogUpdater)239 Fog::DestroyResFogUpdater(os::IAllocator* allocator, ResFogUpdaterData* resFogUpdater)
240 {
241     NW_NULL_ASSERT( allocator );
242     NW_NULL_ASSERT( resFogUpdater );
243 
244 
245     allocator->Free( resFogUpdater );
246 }
247 
248 //-----------------------------------------
249 void
Update(const Camera * camera)250 Fog::Update(const Camera* camera)
251 {
252     NW_ASSERT(this->GetResFog().IsValid());
253     ResFogUpdater fogUpdater = this->GetResFog().GetFogUpdater();
254     NW_ASSERT(fogUpdater.IsValid());
255     ResImageLookupTable fogSampler = this->GetResFog().GetFogSampler();
256     NW_ASSERT(fogSampler.IsValid());
257     NW_ASSERT(fogSampler.GetCommandCacheCount() == NW_FOG_TABLE_COMMAND_SIZE);
258 
259     ResCameraProjectionUpdater resProjectionUpdater =
260         camera->GetProjectionUpdater()->GetResource();
261     NW_ASSERT(resProjectionUpdater.IsValid());
262     f32 near = resProjectionUpdater.GetNear();
263     f32 far = resProjectionUpdater.GetFar();
264     f32 wscale = camera->GetWScale();
265 
266     // フォグの参照テーブルはニアクリップとファークリップとフォグアップデータから再計算されるために
267     // ニアクリップとファークリップとフォグアップデータに変更がなければ参照テーブルは計算しません。
268 
269     ResFogUpdaterData* updaterData = m_UpdaterCache.Get();
270     if (!ut::FloatEqualsWeak(m_Near, near) ||
271         !ut::FloatEqualsWeak(m_Far, far) ||
272         !ut::FloatEqualsWeak(m_WScale, wscale) ||
273         !ut::FloatEqualsWeak(updaterData->m_Density, fogUpdater.GetDensity()) ||
274         !(updaterData->m_FogUpdaterType == fogUpdater.GetFogUpdaterType()) ||
275         !ut::FloatEqualsWeak(updaterData->m_MaxFogDepth, fogUpdater.GetMaxFogDepth()) ||
276         !ut::FloatEqualsWeak(updaterData->m_MinFogDepth, fogUpdater.GetMinFogDepth()))
277     {
278         m_Near = near;
279         m_Far = far;
280         m_WScale = wscale;
281         updaterData->m_Density = fogUpdater.GetDensity();
282         updaterData->m_FogUpdaterType = fogUpdater.GetFogUpdaterType();
283         updaterData->m_MaxFogDepth = fogUpdater.GetMaxFogDepth();
284         updaterData->m_MinFogDepth = fogUpdater.GetMinFogDepth();
285 
286         this->SetupFogSampler(
287             fogSampler,
288             fogUpdater,
289             camera->InverseProjectionMatrix());
290     }
291 }
292 
293 //-----------------------------------------
294 void
SetupFogSampler(ResImageLookupTable fogSampler,ResFogUpdater fogUpdater,const math::MTX44 & inverseProjectionMatrix)295 Fog::SetupFogSampler(
296     ResImageLookupTable fogSampler,
297     ResFogUpdater fogUpdater,
298     const math::MTX44& inverseProjectionMatrix)
299 {
300     const uint HALF_TABLE_SIZE = FOG_TABLE_SIZE / 2;
301 
302 
303     enum
304     {
305         REG_FOG_TABLE_INDEX = 0xe6,
306         REG_FOG_TABLE_PARAM = 0xe8
307     };
308 
309     const u32 HEADER_FOG_TABLE_INDEX = internal::MakeCommandHeader(REG_FOG_TABLE_INDEX, 1, false, 0xF);
310     const u32 HEADER_FOG_TABLE_PARAM = internal::MakeCommandHeader(REG_FOG_TABLE_PARAM, 128, false, 0xF);
311 
312     fogSampler.SetCommandCache(0, 0);
313     fogSampler.SetCommandCache(1, HEADER_FOG_TABLE_INDEX);
314     fogSampler.SetCommandCache(3, HEADER_FOG_TABLE_PARAM);
315 
316     uint index = 0;
317     math::VEC4 viewPos;
318     f32 prevLut = 0.0f;
319 
320     for (int i = 0; i <= HALF_TABLE_SIZE; ++i)
321     {
322         if (m_WScale == 0.0f)
323         {
324             f32 depth = -(f32)i / (f32)( HALF_TABLE_SIZE );
325             viewPos.w = inverseProjectionMatrix.f._32 * depth + inverseProjectionMatrix.f._33;
326             viewPos.z = -(inverseProjectionMatrix.f._22 * depth + inverseProjectionMatrix.f._23) / viewPos.w;
327         }
328         else
329         {
330             viewPos.z = ((f32)i / (f32)HALF_TABLE_SIZE) * (m_Far - m_Near) + m_Near;
331         }
332 
333         if (viewPos.z <= fogUpdater.GetMinFogDepth())
334         {
335             viewPos.z = 1.0f;
336         }
337         else if (viewPos.z > fogUpdater.GetMaxFogDepth())
338         {
339             viewPos.z = 0.0f;
340         }
341         else
342         {
343             if (fogUpdater.GetFogUpdaterType() == ResFogUpdater::FOG_UPDATER_TYPE_LINEAR)
344             {
345                 viewPos.z =
346                     (fogUpdater.GetMaxFogDepth() - viewPos.z) / (fogUpdater.GetMaxFogDepth() - fogUpdater.GetMinFogDepth());
347             }
348             else if (fogUpdater.GetFogUpdaterType() == ResFogUpdater::FOG_UPDATER_TYPE_EXPONENT)
349             {
350                 viewPos.z =
351                     math::FExp(
352                     -fogUpdater.GetDensity() *
353                     (viewPos.z - fogUpdater.GetMinFogDepth()) / (fogUpdater.GetMaxFogDepth() - fogUpdater.GetMinFogDepth()));
354             }
355             else if (fogUpdater.GetFogUpdaterType() == ResFogUpdater::FOG_UPDATER_TYPE_EXPONENT_SQUARE)
356             {
357                 f32 viewDepth = fogUpdater.GetDensity() *
358                         (viewPos.z - fogUpdater.GetMinFogDepth()) / (fogUpdater.GetMaxFogDepth() - fogUpdater.GetMinFogDepth());
359                 viewPos.z =
360                     math::FExp( - viewDepth * viewDepth );
361             }
362             else
363             {
364                 NW_ASSERTMSG(false, "Unknown FogUpdater type.");
365             }
366         }
367 
368         if ( i > 0 )
369         {
370             u32 value = nw::ut::Fixed13::Float32ToFixed13(viewPos.z - prevLut) | (nw::ut::Fixed11::Float32ToFixed11(prevLut) << 13);
371             u32 commandIndex = (index == 0) ? 2 : index + 3;
372 
373             fogSampler.SetCommandCache(commandIndex, value );
374 
375             ++index;
376         }
377 
378         prevLut = viewPos.z;
379     }
380 }
381 
382 //----------------------------------------
383 Result
Initialize(os::IAllocator * allocator)384 Fog::Initialize(os::IAllocator* allocator)
385 {
386     Result result = INITIALIZE_RESULT_OK;
387 
388     result |= TransformNode::Initialize(allocator);
389     NW_ENSURE_AND_RETURN(result);
390 
391     ResFogUpdaterData* resFogUpdater = CreateResFogUpdater(allocator);
392     if (resFogUpdater != NULL)
393     {
394         m_UpdaterCache = ResUpdaterPtr(
395             resFogUpdater,
396             ResFogUpdaterDataDestroyer(allocator));
397     }
398     else
399     {
400         result |= Result::MASK_FAIL_BIT;
401     }
402     NW_ENSURE_AND_RETURN(result);
403 
404     result |= CreateOriginalValue(allocator);
405     NW_ENSURE_AND_RETURN(result);
406 
407     result |= CreateAnimGroup(allocator);
408     NW_ENSURE_AND_RETURN(result);
409 
410     return result;
411 }
412 
413 //----------------------------------------
414 Result
CreateAnimGroup(os::IAllocator * allocator)415 Fog::CreateAnimGroup(os::IAllocator* allocator)
416 {
417     Result result = INITIALIZE_RESULT_OK;
418 
419     AnimBinding* animBinding = GetAnimBinding();
420     if (animBinding == NULL)
421     {
422         return result;
423     }
424 
425     ResFog resFog = GetResFog();
426     NW_ASSERT(resFog.IsValid());
427 
428     NW_ASSERT(resFog.GetAnimGroupsCount() == 1);
429     anim::ResAnimGroup resAnimGroup = resFog.GetAnimGroups(0);
430 
431     NW_ASSERT(resAnimGroup.GetTargetType() == anim::ResGraphicsAnimGroup::TARGET_TYPE_FOG);
432 
433     AnimGroup* animGroup = AnimGroup::Builder()
434         .ResAnimGroup(resAnimGroup)
435         .SetSceneNode(this)
436         .UseOriginalValue(true)
437         .Create(allocator);
438 
439     if (animGroup == NULL)
440     {
441         result |= Result::MASK_FAIL_BIT;
442     }
443 
444     NW_ENSURE_AND_RETURN(result);
445 
446     BindAnim(animGroup);
447 
448     animBinding->SetAnimGroup(0, animGroup);
449     m_AnimGroup = animGroup;
450 
451     return result;
452 }
453 
454 //----------------------------------------
455 Result
CreateOriginalValue(os::IAllocator * allocator)456 Fog::CreateOriginalValue(os::IAllocator* allocator)
457 {
458     Result result = INITIALIZE_RESULT_OK;
459 
460     void* buffer = allocator->Alloc(sizeof(ResFogData));
461     NW_NULL_ASSERT(buffer);
462 
463     // リソースをコピー
464     ResFogData* originalValue = new(buffer) ResFogData(GetResFog().ref());
465     m_OriginalValue = ResFog(originalValue);
466 
467     return result;
468 }
469 
470 //----------------------------------------
471 void
BindAnim(AnimGroup * animGroup)472 Fog::BindAnim(AnimGroup* animGroup)
473 {
474     using namespace anim;
475 
476     const int animMemberCount = animGroup->GetMemberCount();
477     for (int memberIdx = 0; memberIdx < animMemberCount; ++memberIdx)
478     {
479         anim::ResAnimGroupMember resAnimGroupMember =
480             animGroup->GetResAnimGroupMember(memberIdx);
481 
482         switch(resAnimGroupMember.GetObjectType())
483         {
484         case anim::ResAnimGroupMember::OBJECT_TYPE_FOG:
485             {
486                 void* object = GetResFog().ptr();
487                 animGroup->SetTargetObject(memberIdx, object);
488                 u8* target = static_cast<u8*>(object);
489                 target += resAnimGroupMember.GetMemberOffset();
490 
491                 animGroup->SetTargetPtr(memberIdx, target);
492 
493                 u8* originalValue = reinterpret_cast<u8*>(m_OriginalValue.ptr());
494                 originalValue += resAnimGroupMember.GetMemberOffset();
495                 animGroup->SetOriginalValue(memberIdx, originalValue);
496             }
497             break;
498         default:
499             {
500                 NW_FATAL_ERROR("Unknown animation member type");
501             }
502             break;
503         }
504 
505         animGroup->SetTargetObjectIndex(memberIdx, 0);
506     }
507 }
508 
509 //----------------------------------------------------------
510 void
GetMemorySizeInternal(os::MemorySizeCalculator * pSize,ResFog resFog,Description description)511 Fog::GetMemorySizeInternal(
512     os::MemorySizeCalculator* pSize,
513     ResFog resFog,
514     Description description
515 )
516 {
517     NW_ASSERT(description.isFixedSizeMemory);
518 
519     os::MemorySizeCalculator& size = *pSize;
520 
521     size += sizeof(Fog);
522 
523     // FragmentLight::Initialize
524     TransformNode::GetMemorySizeForInitialize(
525         &size,
526         resFog,
527         description);
528 
529     // Fog::CreateResFogUpdater
530     size += sizeof(ResFogUpdaterData);
531 
532     if (description.isAnimationEnabled &&
533         resFog.GetAnimGroupsCount() > 0)
534     {
535         AnimGroup::Builder()
536             .ResAnimGroup(resFog.GetAnimGroups(0))
537             .UseOriginalValue(true)
538             .GetMemorySizeInternal(&size);
539     }
540 
541     // Fog::CreateOriginalValue
542     size += sizeof(ResFogData);
543 }
544 
545 } // namespace gfx
546 } // namespace nw
547