/*---------------------------------------------------------------------------* Project: NintendoWare File: gfx_Model.cpp Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Revision: 26457 $ *---------------------------------------------------------------------------*/ #include "precompiled.h" #include #include #include #include #include #include #include namespace nw { namespace gfx { NW_UT_RUNTIME_TYPEINFO_DEFINITION( Model, TransformNode ); //---------------------------------------- Model* Model::Create( SceneNode* parent, ResSceneObject resource, const Model::Description& description, os::IAllocator* allocator ) { NW_NULL_ASSERT(allocator); ResModel resNode = ResDynamicCast(resource); NW_ASSERT(resNode.IsValid()); // ノード生成 void* memory = allocator->Alloc(sizeof(Model)); NW_NULL_ASSERT(memory); Model* node = new(memory) Model( allocator, resNode, description); Result result = node->Initialize(allocator); NW_ASSERT(result.IsSuccess()); if (parent) { bool isAttached = parent->AttachChild(node); NW_ASSERT(isAttached); } return node; } //---------------------------------------- Result Model::Initialize(os::IAllocator* allocator) { Result result = INITIALIZE_RESULT_OK; result |= TransformNode::Initialize(allocator); NW_ENSURE_AND_RETURN(result); result |= CreateResMeshes(allocator); NW_ENSURE_AND_RETURN(result); result |= CreateResMeshNodeVisibilities(allocator); NW_ENSURE_AND_RETURN(result); result |= CreateMaterials(allocator); NW_ENSURE_AND_RETURN(result); result |= CreateCallbacks(allocator); NW_ENSURE_AND_RETURN(result); result |= CreateAnimGroups(allocator); NW_ENSURE_AND_RETURN(result); result |= CreateMaterialActivator(allocator); NW_ENSURE_AND_RETURN(result); return result; } //---------------------------------------- void Model::Accept( ISceneVisitor* visitor ) { visitor->VisitModel(this); AcceptChildren(visitor); } //---------------------------------------- void Model::BindMaterialAnim(AnimGroup* animGroup) { const int animMemberCount = animGroup->GetMemberCount(); for (int memberIdx = 0; memberIdx < animMemberCount; ++memberIdx) { anim::ResAnimGroupMember resAnimGroupMember = animGroup->GetResAnimGroupMember(memberIdx); const int matIdx = GetMaterialIndex(resAnimGroupMember); Material* pMat = GetMaterial(matIdx); animGroup->SetTargetObjectIndex(memberIdx, matIdx); animGroup->SetTargetPtr(memberIdx, GetMaterialAnimTargetPtr(pMat, resAnimGroupMember, false)); void* object = pMat->GetAnimTargetObject( resAnimGroupMember, pMat->GetActiveResource(resAnimGroupMember.GetObjectType())); animGroup->SetTargetObject(memberIdx, object); // Bufferがあるときは、OriginalValueを使用できる bool useOriginalValue = pMat->CanUseBuffer(resAnimGroupMember.GetObjectType()); if (useOriginalValue) { animGroup->SetOriginalValue( memberIdx, GetMaterialAnimTargetPtr(pMat, resAnimGroupMember, true)); } } } //---------------------------------------- void Model::BindVisibilityAnim(AnimGroup* animGroup) { using namespace anim; const int animMemberCount = animGroup->GetMemberCount(); for (int memberIdx = 0; memberIdx < animMemberCount; ++memberIdx) { anim::ResAnimGroupMember resAnimGroupMember = animGroup->GetResAnimGroupMember(memberIdx); switch(resAnimGroupMember.GetObjectType()) { case anim::ResAnimGroupMember::OBJECT_TYPE_MODEL: { // モデルのビジビリティ // インスタンス側にバインドする // TODO: BranchVisibleもインスタンス側にバインド u8* target = NULL; if (resAnimGroupMember.GetMemberType() == ResModelMember::MEMBER_TYPE_VISIBLE) { target = reinterpret_cast(&m_Visible); } else { target = reinterpret_cast(GetResModel().ptr()); target += resAnimGroupMember.GetMemberOffset(); } // TODO: -1は仮。そもそも、intだけではメンバをユニークに特定できなくなっている。 // インターフェイス自体を見直す必要がある animGroup->SetTargetObjectIndex(memberIdx, -1); animGroup->SetTargetPtr(memberIdx, target); animGroup->SetOriginalValue(memberIdx, &m_OriginalVisibility); } break; case anim::ResAnimGroupMember::OBJECT_TYPE_MESH: { // メッシュのビジビリティ // こちらはバッファにバインドする ResMeshMember meshMember = ResDynamicCast(resAnimGroupMember); NW_ASSERT(meshMember.IsValid()); const int meshIndex = meshMember.GetMeshIndex(); u8* target = reinterpret_cast(GetResMeshes()[meshIndex].ptr()); target += resAnimGroupMember.GetMemberOffset(); animGroup->SetTargetObjectIndex(memberIdx, meshIndex); animGroup->SetTargetPtr(memberIdx, target); animGroup->SetOriginalValue(memberIdx, &m_MeshOriginalVisibilities[meshIndex]); } break; case anim::ResAnimGroupMember::OBJECT_TYPE_MESH_NODE_VISIBILITY: { ResMeshNodeVisibilityMember nodeVisibilityMember = ResDynamicCast(resAnimGroupMember); NW_ASSERT(nodeVisibilityMember.IsValid()); const char* nodeName = nodeVisibilityMember.GetNodeName(); const int visibilityIndex = GetResModel().GetMeshNodeVisibilitiesIndex(nodeName); NW_ASSERT(visibilityIndex >= 0); u8* target = reinterpret_cast(GetResMeshNodeVisibilities(visibilityIndex).ptr()); target += resAnimGroupMember.GetMemberOffset(); animGroup->SetTargetObjectIndex(memberIdx, visibilityIndex); animGroup->SetTargetPtr(memberIdx, target); animGroup->SetOriginalValue(memberIdx, &m_MeshNodeOriginalVisibilities[visibilityIndex]); } break; default: { NW_FATAL_ERROR("Unknown animation member type"); } break; } void* object = GetAnimTargetObject(resAnimGroupMember); animGroup->SetTargetObject(memberIdx, object); } } //---------------------------------------- Result Model::CreateAnimGroups(os::IAllocator* allocator) { Result result = INITIALIZE_RESULT_OK; AnimBinding* animBinding = GetAnimBinding(); if (animBinding == NULL) { return result; } ResModel resModel = GetResModel(); NW_ASSERT(resModel.IsValid()); const int animGroupCount = resModel.GetAnimGroupsCount(); for (int animGroupIdx = 0; animGroupIdx < animGroupCount; ++animGroupIdx) { anim::ResAnimGroup resAnimGroup = resModel.GetAnimGroups(animGroupIdx); const int targetType = resAnimGroup.GetTargetType(); if (targetType == anim::ResGraphicsAnimGroup::TARGET_TYPE_MATERIAL || targetType == anim::ResGraphicsAnimGroup::TARGET_TYPE_VISIBILITY) { AnimGroup* animGroup = AnimGroup::Builder() .ResAnimGroup(resAnimGroup) .SetSceneNode(this) .UseOriginalValue(true) .Create(allocator); if (animGroup == NULL) { result |= Result::MASK_FAIL_BIT; } NW_ENSURE_AND_RETURN(result); // caseが増えるときは、上のifの条件も修正すること switch (targetType) { case anim::ResGraphicsAnimGroup::TARGET_TYPE_MATERIAL: { BindMaterialAnim(animGroup); animBinding->SetAnimGroup(animGroupIdx, animGroup); m_MaterialAnimGroup = animGroup; m_MaterialAnimBindingIndex = animGroupIdx; } break; case anim::ResGraphicsAnimGroup::TARGET_TYPE_VISIBILITY: { BindVisibilityAnim(animGroup); animBinding->SetAnimGroup(animGroupIdx, animGroup); m_VisibilityAnimGroup = animGroup; m_VisibilityAnimBindingIndex = animGroupIdx; } break; default: NW_ASSERT(false); } } } return result; } //---------------------------------------- Result Model::CreateMaterials(os::IAllocator* allocator) { Result result = INITIALIZE_RESULT_OK; if (m_SharingMaterial) { NW_ASSERTMSG(m_BufferOption == 0, "In the case of using a shared material, it can not use buffer option."); m_BufferOption = m_Description.sharedMaterialModel->GetBufferOption(); int materialCount = m_Description.sharedMaterialModel->GetMaterialCount(); NW_ASSERT(materialCount != 0); void* memory = allocator->Alloc(sizeof(Material*) * materialCount); if (memory == NULL) { result |= Result::MASK_FAIL_BIT; } NW_ENSURE_AND_RETURN(result); m_Materials = gfx::MaterialArray(memory, materialCount, allocator); for (int i=0; i < materialCount ; i++) { m_Materials.push_back(m_Description.sharedMaterialModel->GetMaterial(i)); } } else { ResModel resModel = this->GetResModel(); NW_ASSERT(resModel.IsValid()); int materialCount = resModel.GetMaterialsCount(); if (materialCount != 0) { void* memory = allocator->Alloc(sizeof(Material*) * materialCount); if (memory == NULL) { result |= Result::MASK_FAIL_BIT; } NW_ENSURE_AND_RETURN(result); m_Materials = gfx::MaterialArray(memory, materialCount, allocator); s32 bufferCount = (m_BufferOption == 0x0) ? 0 : 1; for (int i=0; i < materialCount ; i++) { Material* material = Material::Create( resModel.GetMaterials(i), bufferCount, this, allocator); if (material == NULL) { result |= Result::MASK_FAIL_BIT; } NW_ENSURE_AND_RETURN(result); m_Materials.push_back(material); } } } return result; } //---------------------------------------- Result Model::CreateResMeshes(os::IAllocator* allocator) { Result result = INITIALIZE_RESULT_OK; ResModel resModel = this->GetResModel(); NW_ASSERT(resModel.IsValid()); s32 meshesCount = resModel.GetMeshesCount(); if (meshesCount != 0) { ut::Offset* meshOffsets = NULL; meshOffsets = allocator->AllocAndFill(meshesCount, ut::Offset()); ResMeshData* buffer = allocator->Alloc(meshesCount); if (meshOffsets == NULL || buffer == NULL) { if (meshOffsets != NULL) { allocator->Free(meshOffsets); } if (buffer != NULL) { allocator->Free(buffer); } result |= Result::MASK_FAIL_BIT; } NW_ENSURE_AND_RETURN(result); for (int i = 0; i < meshesCount; ++i) { buffer[i] = *(resModel.GetMeshes(i).ptr()); buffer[i].toOwnerModel.set_ptr(GetResModel().ptr()); meshOffsets[i].set_ptr(&buffer[i]); } m_MeshBuffers = ResMeshArray(meshOffsets, meshesCount); m_OriginalVisibility = resModel.IsVisible(); void* memory = allocator->Alloc(sizeof(bool) * meshesCount); if (memory == NULL) { result |= Result::MASK_FAIL_BIT; } NW_ENSURE_AND_RETURN(result); m_MeshOriginalVisibilities.Reset(memory, meshesCount, allocator); for (int i = 0; i < meshesCount; ++i) { m_MeshOriginalVisibilities.push_back(m_MeshBuffers[i].IsVisible()); } } return result; } //---------------------------------------- Result Model::CreateResMeshNodeVisibilities(os::IAllocator* allocator) { Result result = INITIALIZE_RESULT_OK; ResModel resModel = this->GetResModel(); NW_ASSERT(resModel.IsValid()); s32 visibilitiesConut = resModel.GetMeshNodeVisibilitiesCount(); if (visibilitiesConut != 0) { ResMeshNodeVisibilityData* buffer = allocator->Alloc(visibilitiesConut); if (buffer == NULL) { result |= Result::MASK_FAIL_BIT; } NW_ENSURE_AND_RETURN(result); m_MeshNodeVisibilityBuffers.Reset(buffer, visibilitiesConut, allocator); for (int i = 0; i < visibilitiesConut; ++i) { m_MeshNodeVisibilityBuffers.push_back(*(resModel.GetMeshNodeVisibilities(i).ptr())); } void* memory = allocator->Alloc(sizeof(bool) * visibilitiesConut); if (memory == NULL) { result |= Result::MASK_FAIL_BIT; } NW_ENSURE_AND_RETURN(result); m_MeshNodeOriginalVisibilities.Reset(memory, visibilitiesConut, allocator); for (int i = 0; i < visibilitiesConut; ++i) { m_MeshNodeOriginalVisibilities.push_back(resModel.GetMeshNodeVisibilities(i).IsVisible()); } } return result; } //---------------------------------------- void Model::DestroyResMeshes(os::IAllocator* allocator, ResMeshArray resMeshes) { NW_NULL_ASSERT(allocator); if (resMeshes.empty()) { return; } // 全てのデータのメモリを解放する。 allocator->Free((*resMeshes.begin()).ptr()); // データのオフセット情報のメモリを解放する。 allocator->Free(static_cast(resMeshes)); } //---------------------------------------- Result Model::CreateCallbacks(os::IAllocator* allocator) { Result result = INITIALIZE_RESULT_OK; if (m_Description.isFixedSizeMemory) { if (m_Description.maxCallbacks == 0) { m_PreRenderSignal = RenderSignal::CreateInvalidateSignal(allocator); m_PostRenderSignal = RenderSignal::CreateInvalidateSignal(allocator); } else { m_PreRenderSignal = RenderSignal::CreateFixedSizedSignal(m_Description.maxCallbacks, allocator); m_PostRenderSignal = RenderSignal::CreateFixedSizedSignal(m_Description.maxCallbacks, allocator); } } else { m_PreRenderSignal = RenderSignal::CreateVariableSizeSignal(allocator); m_PostRenderSignal = RenderSignal::CreateVariableSizeSignal(allocator); } // 動的配列のメモリ確保に失敗した場合 if (m_PreRenderSignal == NULL || m_PostRenderSignal == NULL) { result |= Result::MASK_FAIL_BIT; } return result; } //---------------------------------------- Result Model::CreateMaterialActivator(os::IAllocator* allocator) { Result result = INITIALIZE_RESULT_OK; if (!m_SharingMaterial) { // TODO: Builder でアクティベータを渡せるようにする。 // バッファオプションに応じてアクティベータを変更します。 // 0x0 か FLAG_BUFFER_SCENE_ENVIRONMENT のみ指定された場合は SimpleMaterialActivator を利用します。 IMaterialActivator* activator = (m_Description.bufferOption == 0 || m_Description.bufferOption == FLAG_BUFFER_SCENE_ENVIRONMENT) ? SimpleMaterialActivator::Create(allocator) : MaterialActivator::Create(allocator); if (activator != NULL) { m_MaterialActivator.Reset(activator); } else { result |= Result::MASK_FAIL_BIT; } } return result; } //---------------------------------------- void* Model::GetAnimTargetObject(const anim::ResAnimGroupMember& anim) { switch(anim.GetObjectType()) { case anim::ResAnimGroupMember::OBJECT_TYPE_MODEL: { return this; } case anim::ResAnimGroupMember::OBJECT_TYPE_MESH: { anim::ResMeshMember member = ResStaticCast(anim); int meshIndex = member.GetMeshIndex(); ResMeshData* ptr = GetResMeshes()[meshIndex].ptr(); return ptr; } case anim::ResAnimGroupMember::OBJECT_TYPE_MESH_NODE_VISIBILITY: { anim::ResMeshNodeVisibilityMember member = ResStaticCast(anim); const char* nodeName = member.GetNodeName(); ResMeshNodeVisibilityData* ptr = GetResModel().GetMeshNodeVisibilities(nodeName).ptr(); return ptr; } default: NW_ASSERT(false); return NULL; } } //---------------------------------------- void* Model::GetMaterialAnimTargetPtr(Material* material, const anim::ResAnimGroupMember& anim, bool isOriginalValue) { ResMaterial resMat = isOriginalValue ? material->GetOriginal() : material->GetActiveResource(anim.GetObjectType()); void* object = material->GetAnimTargetObject(anim, resMat); // TextureSamplerのみ、TargetObjectがTextureMapperになっている // TargetPtrはTextureSampler内のメンバを指す必要があるので、変換する if (anim.GetObjectType() == anim::ResAnimGroupMember::OBJECT_TYPE_TEXTURE_SAMPLER) { ResPixelBasedTextureMapper mapper(object); object = mapper.ref().toSampler.to_ptr(); } return ut::AddOffsetToPtr(object, anim.GetMemberOffset()); } //---------------------------------------- int Model::GetMaterialIndex(const anim::ResAnimGroupMember& anim) const { // TODO: GetMaterialName()をResMaterialMemberにくくり出し、分岐を不要にする const char* matName = NULL; switch (anim.GetObjectType()) { case anim::ResAnimGroupMember::OBJECT_TYPE_MATERIAL_COLOR: { anim::ResMaterialColorMember member = ResStaticCast(anim); matName = member.GetMaterialName(); break; } case anim::ResAnimGroupMember::OBJECT_TYPE_TEXTURE_SAMPLER: { anim::ResTextureSamplerMember member = ResStaticCast(anim); matName = member.GetMaterialName(); break; } case anim::ResAnimGroupMember::OBJECT_TYPE_TEXTURE_MAPPER: { anim::ResTextureMapperMember member = ResStaticCast(anim); matName = member.GetMaterialName(); break; } case anim::ResAnimGroupMember::OBJECT_TYPE_BLEND_OPERATION: { anim::ResBlendOperationMember member = ResStaticCast(anim); matName = member.GetMaterialName(); break; } case anim::ResAnimGroupMember::OBJECT_TYPE_TEXTURE_COORDINATOR: { anim::ResTextureCoordinatorMember member = ResStaticCast(anim); matName = member.GetMaterialName(); break; } default: NW_ASSERT(false); return 0; } int matIdx = GetResModel().GetMaterialsIndex(matName); NW_ASSERT(matIdx != -1); return matIdx; } //---------------------------------------- void Model::InvalidateRenderKeyCache() { ResMeshArray::iterator end = this->m_MeshBuffers.end(); for (ResMeshArray::iterator mesh = this->m_MeshBuffers.begin(); mesh != end; ++mesh) { (*mesh).SetFlags( ut::DisableFlag((*mesh).GetFlags(), ResMesh::FLAG_VALID_RENDER_KEY_CACHE)); } } } // namespace gfx } // namespace nw