1 /*---------------------------------------------------------------------------*
2 Project: NintendoWare
3 File: gfx_MeshRenderer.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: 25742 $
14 *---------------------------------------------------------------------------*/
15
16 // パーティクルのストリームに不正値が含まれていないかを調べる定義です。
17 // ストリーム内の全要素を調べるため、デバッグ時のみ有効にしてあります。
18 #ifdef NW_DEBUG
19 #define NW_GFX_CHECK_PARTICLE_STREAMS
20 #endif
21
22 #include "precompiled.h"
23
24 #include <nw/gfx/gfx_Common.h>
25 #include <nw/gfx/res/gfx_ResMesh.h>
26 #include <nw/gfx/res/gfx_ResParticleShape.h>
27 #include <nw/gfx/gfx_SceneObject.h>
28 #include <nw/gfx/gfx_MeshRenderer.h>
29 #include <nw/gfx/gfx_RenderContext.h>
30 #include <nw/gfx/gfx_ShaderProgram.h>
31 #include <nw/gfx/gfx_SkeletalModel.h>
32 #include <nw/gfx/gfx_Skeleton.h>
33 #include <nw/gfx/gfx_ParticleModel.h>
34 #include <nw/gfx/gfx_ParticleShape.h>
35 #include <nw/os/os_Memory.h>
36 #include <nw/types.h>
37 #include <nw/ut/ut_Inlines.h>
38 #include <nw/ut/ut_ResUtil.h>
39 #include <nw/ut/ut_ResDictionary.h>
40 #include <nw/ut/ut_Foreach.h>
41 #include <nw/dev.h>
42 #include <nw/gfx/gfx_CommandUtil.h>
43 #include <nw/gfx/gfx_ActivateCommand.h>
44
45 #include <GLES2/gl2.h>
46 #include <GLES2/gl2ext.h>
47
48 #include <cmath>
49
50 namespace nw
51 {
52 namespace gfx
53 {
54
55 namespace internal
56 {
57
58 //! @brief 頂点シェーダーのレジスタが上書きされていないか確認します。
TestRegisterOverride(int matrixpaletteCount,bool isNonuniformScalable,RenderContext * renderContext)59 bool TestRegisterOverride(int matrixpaletteCount, bool isNonuniformScalable, RenderContext* renderContext)
60 {
61 NW_ASSERT(renderContext->GetShaderProgram()->GetActiveDescription().IsValid());
62
63 int vertexLightEndUniform = renderContext->GetShaderProgram()->GetActiveDescription().GetVertexLightEndUniform();
64 int vertexLightCount = renderContext->GetSceneEnvironment().GetVertexLightCount();
65 const int UNIT_COUNT = 3;
66 const int VERTEX_LIGHT_REGISTER_COUNT = 6;
67 int usedMatrixpaletteCount = 0;
68 if (isNonuniformScalable)
69 {
70 usedMatrixpaletteCount = UNIT_COUNT * matrixpaletteCount * 2 + vertexLightCount * VERTEX_LIGHT_REGISTER_COUNT;
71 }
72 else
73 {
74 usedMatrixpaletteCount = UNIT_COUNT * matrixpaletteCount + vertexLightCount * VERTEX_LIGHT_REGISTER_COUNT;
75 }
76
77 return (usedMatrixpaletteCount <= vertexLightEndUniform);
78 }
79
80 }
81
82 //----------------------------------------
83 MeshRenderer*
Create(nw::os::IAllocator * pAllocator)84 MeshRenderer::Create(nw::os::IAllocator* pAllocator)
85 {
86 NW_NULL_ASSERT(pAllocator);
87
88 size_t size = sizeof(MeshRenderer);
89
90 void* buf = pAllocator->Alloc(size);
91
92 return new(buf) MeshRenderer(pAllocator);
93 }
94
95 //----------------------------------------
96 void
RenderMesh(ResMesh mesh,Model * model)97 MeshRenderer::RenderMesh(ResMesh mesh, Model* model)
98 {
99 NW_ASSERT(mesh.IsValid());
100
101 // Mesh の頂点設定を有効化。
102 ResShape shape = model->GetResModel().GetShapes(mesh.GetShapeIndex());
103
104 switch ( shape.ref().typeInfo )
105 {
106 case ResSeparateDataShape::TYPE_INFO:
107 {
108 m_RenderContext->SetMaterial(model->GetMaterial(mesh.GetMaterialIndex()));
109
110 // コンテキストを有効化します
111 m_RenderContext->ActivateContext();
112
113 if (m_RenderContext->GetModelCache() != model)
114 {
115 m_RenderContext->SetModelMatrix(model);
116 }
117
118 m_RenderContext->ActivateVertexAttribute( mesh );
119
120 RenderSeparateDataShape(
121 model,
122 ResStaticCast<ResSeparateDataShape>(shape),
123 mesh.GetCurrentPrimitiveIndex());
124
125 m_RenderContext->DeactivateVertexAttribute( mesh );
126 }
127 break;
128 case ResParticleShape::TYPE_INFO:
129 {
130 ParticleModel* particleModel = static_cast<ParticleModel*>(model);
131 NW_NULL_ASSERT(particleModel);
132
133 ParticleSet* particleSet = particleModel->GetParticleSets(mesh.GetShapeIndex());
134 if (particleSet->GetParticleCollection()->GetCount() == 0)
135 {
136 return;
137 }
138
139 m_RenderContext->SetMaterial(model->GetMaterial(mesh.GetMaterialIndex()));
140
141 // コンテキストを有効化します
142 m_RenderContext->ActivateParticleContext();
143
144 RenderParticleShape(
145 model,
146 ResStaticCast<ResParticleShape>(shape),
147 mesh.GetShapeIndex());
148 }
149 break;
150 default:
151 {
152 NW_FATAL_ERROR("Unsupported data shape type.");
153 }
154 }
155 }
156
157 //----------------------------------------
158 void
RenderSeparateDataShape(Model * model,ResSeparateDataShape shape,s32 currentPrimitiveIndex)159 MeshRenderer::RenderSeparateDataShape(
160 Model* model,
161 ResSeparateDataShape shape,
162 s32 currentPrimitiveIndex
163 )
164 {
165 NW_NULL_ASSERT(m_RenderContext);
166 if (!shape.IsValid()) { return; }
167
168 const ShaderProgram* shaderProgram = m_RenderContext->GetShaderProgram();
169
170 internal::NWSetVertexUniform3fv( VERTEX_SHADER_UNIFORM_POSOFFS_INDEX, 1, shape.GetPositionOffset() );
171
172 SkeletalModel* skeletalModel = ut::DynamicCast<SkeletalModel*>(model);
173
174 ResPrimitiveSetArray primitiveSets = shape.GetPrimitiveSets();
175 ResPrimitiveSetArray::iterator primitiveSetEnd = primitiveSets.end();
176
177 for (ResPrimitiveSetArray::iterator primitiveSet = primitiveSets.begin();
178 primitiveSet != primitiveSetEnd; ++primitiveSet)
179 {
180 NW_ASSERT((*primitiveSet).IsValid());
181 NW_ASSERT(0 <= currentPrimitiveIndex &&
182 currentPrimitiveIndex < (*primitiveSet).GetPrimitivesCount());
183
184 s32 boneIndexCount = (*primitiveSet).GetBoneIndexTableCount();
185
186 NW_ASSERTMSG(
187 internal::TestRegisterOverride(
188 boneIndexCount,
189 model->GetResModel().IsNonuniformScalable(),
190 m_RenderContext
191 ),
192 "Vertex-lights or user-registers might be overridden by bone matrices.%s", model->GetResModel().GetName()
193 );
194
195 if (skeletalModel && boneIndexCount != 0)
196 {
197 this->SetMatrixPalette(
198 skeletalModel,
199 *primitiveSet,
200 boneIndexCount
201 );
202 }
203 else
204 {
205 shaderProgram->SetVertexUniformBool(NW_GFX_VERTEX_UNIFORM(ISSMOSK), false);
206 shaderProgram->SetVertexUniformBool(NW_GFX_VERTEX_UNIFORM(ISRGDSK), false);
207
208 m_RenderContext->SetMatrixPaletteCount(1);
209 #ifdef NW_GFX_MODEL_TRANSLATE_OFFSET_ENABLED
210 math::MTX34 worldMatrix(model->WorldMatrix());
211 MTX34MultTranslate(&worldMatrix, m_RenderContext->ModelTranslateOffset(), worldMatrix);
212 shaderProgram->SetUniversal(0, worldMatrix);
213 #else
214 shaderProgram->SetUniversal(0, model->WorldMatrix());
215 #endif
216 }
217
218 m_RenderContext->RenderPrimitive((*primitiveSet).GetPrimitives(currentPrimitiveIndex));
219 }
220 }
221
222 //----------------------------------------
223 namespace internal
224 {
225 static bool
isIllegal(f32 val)226 isIllegal(f32 val)
227 {
228 // return isnan(val);
229 return !isfinite(val);
230 }
231
232 static bool
isIllegal(const math::VEC3 & vec)233 isIllegal(const math::VEC3& vec)
234 {
235 return isIllegal(vec.x) || isIllegal(vec.y) || isIllegal(vec.z);
236 }
237
238 static bool
isIllegal(const math::MTX34 & mtx)239 isIllegal(const math::MTX34& mtx)
240 {
241 return
242 isIllegal(mtx._00) || isIllegal(mtx._01) || isIllegal(mtx._02) || isIllegal(mtx._03) ||
243 isIllegal(mtx._10) || isIllegal(mtx._11) || isIllegal(mtx._12) || isIllegal(mtx._13) ||
244 isIllegal(mtx._20) || isIllegal(mtx._21) || isIllegal(mtx._22) || isIllegal(mtx._23);
245 }
246 }
247
248 //----------------------------------------
249 void
RenderParticleShape(Model * model,ResParticleShape resource,int shapeIndex)250 MeshRenderer::RenderParticleShape(
251 Model* model,
252 ResParticleShape resource,
253 int shapeIndex
254 )
255 {
256 NW_NULL_ASSERT(m_RenderContext);
257 if (!resource.IsValid()) { return; }
258
259 ParticleModel* particleModel = static_cast<ParticleModel*>(model);
260 NW_NULL_ASSERT(particleModel);
261
262 ParticleSet* particleSet = particleModel->GetParticleSets(shapeIndex);
263
264 const ResParticleSet& resParticleSet = particleSet->GetResParticleSet();
265
266 ParticleShape* particleShape = particleModel->GetParticleShapes(shapeIndex);
267 NW_NULL_ASSERT(particleShape);
268
269 NW_ASSERT(particleShape->GetResParticleShape().ptr() == resource.ptr());
270
271 if (particleSet->GetParticleCollection()->GetCount() == 0)
272 {
273 return;
274 }
275
276 if (resParticleSet.GetIsBufferFlushEnabled())
277 {
278 particleShape->FlushBuffer();
279 }
280
281 int bufferSide = particleShape->GetBufferSide();
282 internal::NWUseCmdlist(particleShape->m_CommandCache[bufferSide], particleShape->m_CommandCacheSize[bufferSide]);
283
284 enum
285 {
286 REG_UNIFORM_FLOAT_INDEX = 0x2c0,
287 REG_VS_FLOAT_DATA1 = 0x2c1,
288 REG_INDEX_STREAM_OFFSET = 0x227,
289 REG_INDEX_STREAM_COUNT = 0x228
290 };
291
292 // c6-c7
293 u32* command = (u32*)internal::NWGetCurrentCmdBuffer();
294 int commandIndex = 0;
295
296 command[commandIndex++] = 0x80000000 | VERTEX_SHADER_UNIFORM_POSOFFS_INDEX;
297 command[commandIndex++] = internal::MakeCommandHeader(REG_UNIFORM_FLOAT_INDEX, 1 + 4, true, 0xF);
298 command[commandIndex++] = 0;
299 command[commandIndex++] = *(u32*)&resource.GetPositionOffset().z;
300 command[commandIndex++] = *(u32*)&resource.GetPositionOffset().y;
301 command[commandIndex++] = *(u32*)&resource.GetPositionOffset().x;
302 NW_ASSERT(!internal::isIllegal(resource.GetPositionOffset()));
303
304 // universal
305 Camera* camera = m_RenderContext->GetActiveCamera();
306 NW_NULL_ASSERT(camera);
307
308 const u32 HEADER_UNIFORM_FLOAT_INDEX = internal::MakeCommandHeader(REG_UNIFORM_FLOAT_INDEX, 1, false, 0xF);
309
310 command[commandIndex++] = 0x80000000 | VERTEX_SHADER_UNIFORM_UNIVREG_INDEX;
311 command[commandIndex++] = HEADER_UNIFORM_FLOAT_INDEX;
312
313 const int universalNum = 15;
314
315 if (particleSet->GetResParticleSet().GetIsForceWorld())
316 {
317 // modelを強制的に単位行列とする
318
319 // view . model = view . I = view
320 NW_ASSERT(!internal::isIllegal(camera->ViewMatrix()));
321 internal::NWCopyMtx34WithHeader(
322 (f32*)&command[commandIndex],
323 (f32*)&camera->ViewMatrix(),
324 internal::MakeCommandHeader(REG_VS_FLOAT_DATA1, 4 * universalNum, false, 0xF));
325 commandIndex += 12 + 1;
326
327 // Inv(view . model) = Inv(view . I) = Inv(view)
328 NW_ASSERT(!internal::isIllegal(camera->InverseViewMatrix()));
329 internal::NWCopyMtx34Reverse(
330 (f32*)&command[commandIndex],
331 (f32*)&camera->InverseViewMatrix());
332 commandIndex += 12;
333
334 // Inv(model)
335 command[commandIndex++] = 0;
336 command[commandIndex++] = 0;
337 command[commandIndex++] = 0;
338 command[commandIndex++] = 0x3f800000;
339
340 command[commandIndex++] = 0;
341 command[commandIndex++] = 0;
342 command[commandIndex++] = 0x3f800000;
343 command[commandIndex++] = 0;
344
345 command[commandIndex++] = 0;
346 command[commandIndex++] = 0x3f800000;
347 command[commandIndex++] = 0;
348 command[commandIndex++] = 0;
349
350 // model
351 command[commandIndex++] = 0;
352 command[commandIndex++] = 0;
353 command[commandIndex++] = 0;
354 command[commandIndex++] = 0x3f800000;
355
356 command[commandIndex++] = 0;
357 command[commandIndex++] = 0;
358 command[commandIndex++] = 0x3f800000;
359 command[commandIndex++] = 0;
360
361 command[commandIndex++] = 0;
362 command[commandIndex++] = 0x3f800000;
363 command[commandIndex++] = 0;
364 command[commandIndex++] = 0;
365 }
366 else
367 {
368 nw::math::MTX34* worldMatrix;
369
370 #ifdef NW_GFX_MODEL_TRANSLATE_OFFSET_ENABLED
371 math::MTX34 offsetMatrix(model->WorldMatrix());
372 worldMatrix = &offsetMatrix;
373 MTX34MultTranslate(worldMatrix, m_RenderContext->ModelTranslateOffset(), *worldMatrix);
374 #else
375 worldMatrix = &model->WorldMatrix();
376 #endif
377
378 nw::math::MTX34 modelView;
379 nw::math::MTX34Mult(&modelView, camera->ViewMatrix(), *worldMatrix);
380 NW_ASSERT(!internal::isIllegal(modelView));
381 internal::NWCopyMtx34WithHeader(
382 (f32*)&command[commandIndex],
383 (f32*)&modelView,
384 internal::MakeCommandHeader(REG_VS_FLOAT_DATA1, 4 * universalNum, false, 0xF));
385 commandIndex += 12 + 1;
386
387 // Inv(view . model) = Inv(model) . Inv(view)
388 nw::math::MTX34 invModelView;
389
390 #ifdef NW_GFX_MODEL_TRANSLATE_OFFSET_ENABLED
391 MTX34MultTranslate(&invModelView, -m_RenderContext->ModelTranslateOffset(), model->InverseWorldMatrix());
392 nw::math::MTX34Mult(&invModelView, invModelView, camera->InverseViewMatrix());
393 #else
394 nw::math::MTX34Mult(&invModelView, model->InverseWorldMatrix(), camera->InverseViewMatrix());
395 #endif
396
397 NW_ASSERT(!internal::isIllegal(invModelView));
398 internal::NWCopyMtx34Reverse(
399 (f32*)&command[commandIndex],
400 (f32*)&invModelView);
401 commandIndex += 12;
402
403 // Inv(model)
404 NW_ASSERT(!internal::isIllegal(model->InverseWorldMatrix()));
405 internal::NWCopyMtx34Reverse(
406 (f32*)&command[commandIndex],
407 (f32*)&model->InverseWorldMatrix());
408 commandIndex += 12;
409
410 // model
411 NW_ASSERT(!internal::isIllegal(model->WorldMatrix()));
412 internal::NWCopyMtx34Reverse(
413 (f32*)&command[commandIndex],
414 (f32*)worldMatrix);
415 commandIndex += 12;
416 }
417
418 math::VEC3 offset(0.0f, 0.0f, 0.0f);
419 const ResParticleShapeBuilder& resShapeBuilder = resParticleSet.GetParticleShapeBuilder();
420 if (resShapeBuilder.IsValid())
421 {
422 offset = resShapeBuilder.GetDrawOffset();
423 }
424
425 NW_ASSERT(!internal::isIllegal(offset));
426 internal::NWCopyVec3Reverse(
427 (f32*)&command[commandIndex],
428 (f32*)&offset);
429 commandIndex += 4;
430
431 NW_ASSERT(!internal::isIllegal(particleSet->GetScaleOffset()));
432 internal::NWCopyVec3Reverse(
433 (f32*)&command[commandIndex],
434 (f32*)&particleSet->GetScaleOffset());
435 commandIndex += 4;
436
437 NW_ASSERT(!internal::isIllegal(particleSet->GetRotateOffset()));
438 internal::NWCopyVec3Reverse(
439 (f32*)&command[commandIndex],
440 (f32*)&particleSet->GetRotateOffset());
441 commandIndex += 4;
442
443 if ((commandIndex & 1) == 1)
444 {
445 command[commandIndex++] = 0; // padding
446 }
447
448 const u32 HEADER_INDEX_STREAM_COUNT = internal::MakeCommandHeader(REG_INDEX_STREAM_COUNT, 1, false, 0xF);
449
450 command[commandIndex++] = particleSet->GetParticleCollection()->GetCount();
451 command[commandIndex++] = HEADER_INDEX_STREAM_COUNT;
452
453 u32 streamOffset = particleShape->GetPrimitiveStreamOffset(PARTICLE_BUFFER_FRONT);
454
455 const bool isAscendingOrder = (resShapeBuilder.IsValid())? resShapeBuilder.IsAscendingOrder() : true;
456 if (!isAscendingOrder)
457 {
458 ParticleCollection* collection = particleSet->GetParticleCollection();
459 streamOffset += sizeof(u16) * (collection->GetCapacity() - collection->GetCount());
460 }
461
462 const u32 HEADER_INDEX_STREAM_OFFSET = internal::MakeCommandHeader(REG_INDEX_STREAM_OFFSET, 1, false, 0xF);
463
464 command[commandIndex++] = 0x80000000 | streamOffset;
465 command[commandIndex++] = HEADER_INDEX_STREAM_OFFSET;
466
467 internal::NWForwardCurrentCmdBuffer(sizeof(u32) * commandIndex);
468
469 #ifdef NW_GFX_CHECK_PARTICLE_STREAMS
470 for (int index = 0; index < particleShape->GetVertexAttributesCount(); ++index)
471 {
472 int format = particleShape->GetVertexAttributeFormatType(index);
473 if (format != GL_FLOAT)
474 {
475 continue;
476 }
477
478 if (particleShape->IsVertexStream(index))
479 {
480 // stream
481 int count = particleShape->GetVertexCapacity();
482 count *= particleShape->GetVertexAttributeDimension(index);
483
484 const f32* ptr = reinterpret_cast<const f32*>(
485 particleShape->GetVertexStreamPtr(index, PARTICLE_BUFFER_FRONT));
486 for (int i = 0; i < count; ++i)
487 {
488 NW_ASSERT(!internal::isIllegal(ptr[i]));
489 }
490 }
491 else
492 {
493 // param
494 int count = 1;
495 count *= particleShape->GetVertexAttributeDimension(index);
496
497 const f32* ptr = particleShape->GetVertexParameter(index);
498 for (int i = 0; i < count; ++i)
499 {
500 NW_ASSERT(!internal::isIllegal(ptr[i]));
501 }
502 }
503 }
504 #endif
505
506 internal::NWUseCmdlist(
507 particleShape->m_PrimitiveCommandCache,
508 particleShape->m_PrimitiveCommandCacheSize);
509
510 internal::NWUseCmdlist(
511 particleShape->m_DeactivateVertexCommandCache,
512 particleShape->m_DeactivateVertexCommandCacheSize);
513 }
514
515 //----------------------------------------
516 void
SetMatrixPalette(SkeletalModel * skeletalModel,ResPrimitiveSet primitiveSet,s32 boneIndexCount)517 MeshRenderer::SetMatrixPalette(
518 SkeletalModel* skeletalModel,
519 ResPrimitiveSet primitiveSet,
520 s32 boneIndexCount)
521 {
522 NW_NULL_ASSERT(m_RenderContext);
523 NW_ASSERT(primitiveSet.IsValid());
524
525 bool useNormalMatrix = false;
526 const int UNIT_COUNT = 3;
527
528 //----------------------------------------
529 // マトリクスパレットに関する設定用レジスタをシェーダに設定します。
530 const ShaderProgram* shaderProgram = m_RenderContext->GetShaderProgram();
531
532 //bool isRigid = boneIndexCount == 1 || primitiveSet.GetSkinningMode() != ResPrimitiveSet::SKINNING_MODE_SMOOTH;
533 bool isRigid = primitiveSet.GetSkinningMode() != ResPrimitiveSet::SKINNING_MODE_SMOOTH;
534 shaderProgram->SetVertexUniformBool(NW_GFX_VERTEX_UNIFORM(ISSMOSK), !isRigid);
535 shaderProgram->SetVertexUniformBool(NW_GFX_VERTEX_UNIFORM(ISRGDSK), isRigid);
536
537 m_RenderContext->SetMatrixPaletteCount(boneIndexCount);
538
539 //----------------------------------------
540 // ボーンの種類に合わせたマトリクスを取得し、シェーダに設定します。
541 Skeleton* skeleton = skeletalModel->GetSkeleton();
542 ResSkeleton resSkeleton = skeleton->GetResSkeleton();
543 Skeleton::MatrixPose& matrixPose = skeleton->WorldMatrixPose();
544 Skeleton::MatrixPose& skiningPose = skeleton->SkiningMatrixPose();
545
546 const int normalMatrixOffset = boneIndexCount * UNIT_COUNT;
547
548 #ifdef NW_GFX_MODEL_TRANSLATE_OFFSET_ENABLED
549 const bool isModelCoordinate =
550 ut::CheckFlag(resSkeleton.GetFlags(), ResSkeletonData::FLAG_MODEL_COORDINATE);
551 #endif
552
553 for (int count = 0; count < boneIndexCount; ++count)
554 {
555 s32 boneIndex = primitiveSet.GetBoneIndexTable(count);
556
557 // マトリクスを取得してきます。
558 ResBone bone = resSkeleton.GetBones(boneIndex);
559 bool hasSkinningMatrix = (bone.GetFlags() & ResBoneData::FLAG_HAS_SKINNING_MATRIX) != 0;
560 Skeleton::MatrixPose* pose =
561 (!isRigid && hasSkinningMatrix) ? &skiningPose : &matrixPose;
562
563 math::MTX34* matrix;
564
565 #ifdef NW_GFX_MODEL_TRANSLATE_OFFSET_ENABLED
566 math::MTX34 offsetMatrix;
567 if (isModelCoordinate)
568 {
569 matrix = pose->GetMatrix(boneIndex);
570 }
571 else
572 {
573 offsetMatrix = *pose->GetMatrix(boneIndex);
574 matrix = &offsetMatrix;
575 MTX34MultTranslate(matrix, m_RenderContext->ModelTranslateOffset(), *matrix);
576 }
577 #else
578 matrix = pose->GetMatrix(boneIndex);
579 #endif
580
581 // マトリクスを設定します。
582 int index = count * UNIT_COUNT;
583
584 if (count == 0)
585 {
586 internal::NWSetVertexUniform4fvBegin(
587 VERTEX_SHADER_UNIFORM_UNIVREG_INDEX + index,
588 boneIndexCount * UNIT_COUNT,
589 UNIT_COUNT,
590 *matrix);
591 }
592 else
593 {
594 internal::NWSetVertexUniform4fvContinuous(UNIT_COUNT, *matrix);
595 }
596
597 if (useNormalMatrix)
598 {
599 // TODO: 現状ではここには到達しない。
600 math::MTX34 normalMatrix(*pose->GetMatrix(boneIndex));
601 math::MTX34Inverse(&normalMatrix, normalMatrix);
602 math::MTX34Transpose(&normalMatrix, normalMatrix);
603 shaderProgram->SetUniversal(
604 normalMatrixOffset + index, normalMatrix);
605 }
606 }
607
608 internal::NWSetVertexUniform4fvEnd();
609 }
610
611 } // namespace gfx
612 } // namespace nw
613
614