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