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