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: 25151 $
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 void
Accept(ISceneVisitor * visitor)61 Fog::Accept(
62     ISceneVisitor* visitor
63 )
64 {
65     visitor->VisitFog(this);
66     AcceptChildren(visitor);
67 }
68 
69 //----------------------------------------
70 /* static */ Fog*
Create(SceneNode * parent,ResSceneObject resource,const Fog::Description & description,os::IAllocator * allocator)71 Fog::Create(
72     SceneNode* parent,
73     ResSceneObject resource,
74     const Fog::Description& description,
75     os::IAllocator* allocator
76 )
77 {
78     NW_NULL_ASSERT(allocator);
79 
80     ResFog resNode = ResDynamicCast<ResFog>(resource);
81     NW_ASSERT(resNode.IsValid());
82     NW_ASSERT( internal::ResCheckRevision( resNode ) );
83 
84     void* memory = allocator->Alloc(sizeof(Fog));
85     NW_NULL_ASSERT(memory);
86 
87     Fog* fog = new(memory) Fog(
88         allocator,
89         resNode,
90         description);
91     Result result = fog->Initialize(allocator);
92     NW_ASSERT(result.IsSuccess());
93 
94     if (parent)
95     {
96         bool isAttached = parent->AttachChild(fog);
97         NW_ASSERT(isAttached);
98     }
99 
100     return fog;
101 }
102 
103 //-----------------------------------------
104 /* static*/ ResFogData*
CreateResFog(os::IAllocator * allocator,const char * name)105 Fog::CreateResFog(os::IAllocator* allocator, const char* name /* = NULL */)
106 {
107     // TODO: 細かくアロケートする実装になっているが、まとめて確保できる仕組みも追加予定。
108 
109     ResFogData* resFog =
110         AllocateAndFillN<ResFogData>(allocator, sizeof(ResFogData), 0);
111 
112     //--------------------------------
113     // ResSceneObjectData のメンバ初期化
114     resFog->typeInfo = ResFog::TYPE_INFO;
115     resFog->m_Header.revision = ResFog::BINARY_REVISION;
116     resFog->m_Header.signature = ResFog::SIGNATURE;
117 
118     resFog->m_UserDataDicCount = 0;
119     resFog->toUserDataDic.set_ptr( NULL );
120 
121     resFog->toName.set_ptr(AllocateAndCopyString(name, allocator, MAX_NAME_LENGTH));
122 
123     //--------------------------------
124     // ResSceneNodeData のメンバ初期化
125     resFog->m_ChildrenTableCount = 0;
126     resFog->toChildrenTable.set_ptr( NULL );
127     resFog->m_AnimGroupsDicCount = NULL;
128     resFog->toAnimGroupsDic.set_ptr( NULL );
129 
130     //--------------------------------
131     // ResTransformNode のメンバ初期化
132     const math::VEC3 scale(1.0f, 1.0f, 1.0f);
133     const math::VEC3 rotate(0.0f, 0.0f, 0.0f);
134     const math::VEC3 translate(0.0f, 0.0, 0.0f);
135     resFog->m_Transform = math::Transform3(scale, rotate, translate);
136     resFog->m_WorldMatrix = math::MTX34::Identity();
137 
138     //--------------------------------
139     // ResFogData のメンバ初期化
140     ResImageLookupTableData* fogSampler =
141         AllocateAndFill<ResImageLookupTableData>(allocator, 0);
142 
143     ResFogUpdaterData* fogUpdater =
144         AllocateAndFill<ResFogUpdaterData>(allocator, 0);
145 
146     resFog->toFogUpdater.set_ptr( fogUpdater );
147     resFog->toFogSampler.set_ptr( fogSampler );
148 
149     //--------------------------------
150     // ResImageLookupTableData のメンバ初期化
151     fogSampler->typeInfo = ResImageLookupTable::TYPE_INFO;
152 
153     void* tableMemory = allocator->AllocAndFill(sizeof(u32) * NW_FOG_TABLE_COMMAND_NUM, 0);
154     fogSampler->m_CommandCacheTableCount = NW_FOG_TABLE_COMMAND_SIZE;
155     fogSampler->toCommandCacheTable.set_ptr( tableMemory );
156 
157     return resFog;
158 }
159 
160 //-----------------------------------------
161 /* static */ void
DestroyResFog(os::IAllocator * allocator,ResFogData * resFog)162 Fog::DestroyResFog(os::IAllocator* allocator, ResFogData* resFog)
163 {
164     NW_NULL_ASSERT( allocator );
165     NW_NULL_ASSERT( resFog );
166 
167     if ( resFog->toName.to_ptr() != NULL )
168     {
169         allocator->Free( const_cast<char*>( resFog->toName.to_ptr() ) );
170     }
171 
172     if (resFog->toFogUpdater.to_ptr() != NULL)
173     {
174         allocator->Free( resFog->toFogUpdater.to_ptr() );
175     }
176 
177     if (resFog->toFogSampler.to_ptr() != NULL)
178     {
179         ResImageLookupTable fogSampler = ResImageLookupTable(resFog->toFogSampler.to_ptr());
180         if (fogSampler.ref().toCommandCacheTable.to_ptr() != NULL)
181         {
182             allocator->Free( fogSampler.ref().toCommandCacheTable.to_ptr() );
183         }
184 
185         allocator->Free( resFog->toFogSampler.to_ptr() );
186     }
187 
188     allocator->Free( resFog );
189 }
190 //-----------------------------------------
191 /* static*/ ResFogUpdaterData*
CreateResFogUpdater(os::IAllocator * allocator)192 Fog::CreateResFogUpdater(os::IAllocator* allocator)
193 {
194     ResFogUpdaterData* fogUpdater =
195         AllocateAndFill<ResFogUpdaterData>(allocator, 0);
196 
197     NW_NULL_ASSERT(fogUpdater);
198 
199     return fogUpdater;
200 }
201 
202 //-----------------------------------------
203 /* static */ void
DestroyResFogUpdater(os::IAllocator * allocator,ResFogUpdaterData * resFogUpdater)204 Fog::DestroyResFogUpdater(os::IAllocator* allocator, ResFogUpdaterData* resFogUpdater)
205 {
206     NW_NULL_ASSERT( allocator );
207     NW_NULL_ASSERT( resFogUpdater );
208 
209 
210     allocator->Free( resFogUpdater );
211 }
212 
213 //-----------------------------------------
214 void
Update(const Camera * camera)215 Fog::Update(const Camera* camera)
216 {
217     NW_ASSERT(this->GetResFog().IsValid());
218     ResFogUpdater fogUpdater = this->GetResFog().GetFogUpdater();
219     NW_ASSERT(fogUpdater.IsValid());
220     ResImageLookupTable fogSampler = this->GetResFog().GetFogSampler();
221     NW_ASSERT(fogSampler.IsValid());
222     NW_ASSERT(fogSampler.GetCommandCacheCount() == NW_FOG_TABLE_COMMAND_SIZE);
223 
224     ResCameraProjectionUpdater resProjectionUpdater =
225         camera->GetProjectionUpdater()->GetResource();
226     NW_ASSERT(resProjectionUpdater.IsValid());
227     f32 near = resProjectionUpdater.GetNear();
228     f32 far = resProjectionUpdater.GetFar();
229     f32 wscale = camera->GetWScale();
230 
231     // フォグの参照テーブルはニアクリップとファークリップとフォグアップデータから再計算されるために
232     // ニアクリップとファークリップとフォグアップデータに変更がなければ参照テーブルは計算しません。
233 
234     ResFogUpdaterData* updaterData = m_UpdaterCache.Get();
235     if (!ut::FloatEqualsWeak(m_Near, near) ||
236         !ut::FloatEqualsWeak(m_Far, far) ||
237         !ut::FloatEqualsWeak(m_WScale, wscale) ||
238         !ut::FloatEqualsWeak(updaterData->m_Density, fogUpdater.GetDensity()) ||
239         !(updaterData->m_FogUpdaterType == fogUpdater.GetFogUpdaterType()) ||
240         !ut::FloatEqualsWeak(updaterData->m_MaxFogDepth, fogUpdater.GetMaxFogDepth()) ||
241         !ut::FloatEqualsWeak(updaterData->m_MinFogDepth, fogUpdater.GetMinFogDepth()))
242     {
243         m_Near = near;
244         m_Far = far;
245         m_WScale = wscale;
246         updaterData->m_Density = fogUpdater.GetDensity();
247         updaterData->m_FogUpdaterType = fogUpdater.GetFogUpdaterType();
248         updaterData->m_MaxFogDepth = fogUpdater.GetMaxFogDepth();
249         updaterData->m_MinFogDepth = fogUpdater.GetMinFogDepth();
250 
251         this->SetupFogSampler(
252             fogSampler,
253             fogUpdater,
254             camera->InverseProjectionMatrix());
255     }
256 }
257 
258 //-----------------------------------------
259 void
SetupFogSampler(ResImageLookupTable fogSampler,ResFogUpdater fogUpdater,const math::MTX44 & inverseProjectionMatrix)260 Fog::SetupFogSampler(
261     ResImageLookupTable fogSampler,
262     ResFogUpdater fogUpdater,
263     const math::MTX44& inverseProjectionMatrix)
264 {
265     const uint HALF_TABLE_SIZE = FOG_TABLE_SIZE / 2;
266 
267 
268     enum
269     {
270         REG_FOG_TABLE_INDEX = 0xe6,
271         REG_FOG_TABLE_PARAM = 0xe8
272     };
273 
274     const u32 HEADER_FOG_TABLE_INDEX = internal::MakeCommandHeader(REG_FOG_TABLE_INDEX, 1, false, 0xF);
275     const u32 HEADER_FOG_TABLE_PARAM = internal::MakeCommandHeader(REG_FOG_TABLE_PARAM, 128, false, 0xF);
276 
277     fogSampler.SetCommandCache(0, 0);
278     fogSampler.SetCommandCache(1, HEADER_FOG_TABLE_INDEX);
279     fogSampler.SetCommandCache(3, HEADER_FOG_TABLE_PARAM);
280 
281     uint index = 0;
282     math::VEC4 viewPos;
283     f32 prevLut = 0.0f;
284 
285     for (int i = 0; i <= HALF_TABLE_SIZE; ++i)
286     {
287         if (m_WScale == 0.0f)
288         {
289             f32 depth = -(f32)i / (f32)( HALF_TABLE_SIZE );
290             viewPos.w = inverseProjectionMatrix.f._32 * depth + inverseProjectionMatrix.f._33;
291             viewPos.z = -(inverseProjectionMatrix.f._22 * depth + inverseProjectionMatrix.f._23) / viewPos.w;
292         }
293         else
294         {
295             viewPos.z = ((f32)i / (f32)HALF_TABLE_SIZE) * (m_Far - m_Near) + m_Near;
296         }
297 
298         if (viewPos.z <= fogUpdater.GetMinFogDepth())
299         {
300             viewPos.z = 1.0f;
301         }
302         else if (viewPos.z > fogUpdater.GetMaxFogDepth())
303         {
304             viewPos.z = 0.0f;
305         }
306         else
307         {
308             if (fogUpdater.GetFogUpdaterType() == ResFogUpdater::FOG_UPDATER_TYPE_LINEAR)
309             {
310                 viewPos.z =
311                     (fogUpdater.GetMaxFogDepth() - viewPos.z) / (fogUpdater.GetMaxFogDepth() - fogUpdater.GetMinFogDepth());
312             }
313             else if (fogUpdater.GetFogUpdaterType() == ResFogUpdater::FOG_UPDATER_TYPE_EXPONENT)
314             {
315                 viewPos.z =
316                     math::FExp(
317                     -fogUpdater.GetDensity() *
318                     (viewPos.z - fogUpdater.GetMinFogDepth()) / (fogUpdater.GetMaxFogDepth() - fogUpdater.GetMinFogDepth()));
319             }
320             else if (fogUpdater.GetFogUpdaterType() == ResFogUpdater::FOG_UPDATER_TYPE_EXPONENT_SQUARE)
321             {
322                 f32 viewDepth = fogUpdater.GetDensity() *
323                         (viewPos.z - fogUpdater.GetMinFogDepth()) / (fogUpdater.GetMaxFogDepth() - fogUpdater.GetMinFogDepth());
324                 viewPos.z =
325                     math::FExp( - viewDepth * viewDepth );
326             }
327             else
328             {
329                 NW_ASSERTMSG(false, "Unknown FogUpdater type.");
330             }
331         }
332 
333         if ( i > 0 )
334         {
335             u32 value = nw::ut::Fixed13::Float32ToFixed13(viewPos.z - prevLut) | (nw::ut::Fixed11::Float32ToFixed11(prevLut) << 13);
336             u32 commandIndex = (index == 0) ? 2 : index + 3;
337 
338             fogSampler.SetCommandCache(commandIndex, value );
339 
340             ++index;
341         }
342 
343         prevLut = viewPos.z;
344     }
345 }
346 
347 //----------------------------------------
348 Result
Initialize(os::IAllocator * allocator)349 Fog::Initialize(os::IAllocator* allocator)
350 {
351     Result result = INITIALIZE_RESULT_OK;
352 
353     result |= TransformNode::Initialize(allocator);
354     NW_ENSURE_AND_RETURN(result);
355 
356     ResFogUpdaterData* resFogUpdater = CreateResFogUpdater(allocator);
357     if (resFogUpdater != NULL)
358     {
359         m_UpdaterCache = ResUpdaterPtr(
360             resFogUpdater,
361             ResFogUpdaterDataDestroyer(allocator));
362     }
363     else
364     {
365         result |= Result::MASK_FAIL_BIT;
366     }
367     NW_ENSURE_AND_RETURN(result);
368 
369     return result;
370 }
371 
372 } // namespace gfx
373 } // namespace nw
374