/**************************************************************************** Copyright (c) 2011-2013,WebJet Business Division,CYOU http://www.genesis-3d.com.cn Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ****************************************************************************/ #ifndef __PROJECTOR_COMMIT__ #include "stdneb.h" #include "projectorcomponent.h" #include "graphicfeature/graphicsfeatureprotocol.h" #include "graphicfeature/graphicsfeature.h" #include "rendersystem/RenderSystem.h" #if __ANDROID__ #define ICE_NO_DLL #include "OPCODEA/Opcode.h" #elif __OSX__ #include "Opcode.h" #elif __WIN32__ #include "Opcode/Opcode.h" #endif #include "graphicsystem/Projector/CollisionModelServer.h" #include "app/graphicfeature/components/skinnedmeshrendercomponent.h" #include "app/graphicfeature/components/projectorcomponent.h" #include "app/graphicfeature/components/projectorrenderobject.h" #include "resource/meshres.h" #include "app/terrainfeature/components/TerrainRenderObject.h" namespace App { using namespace Resources; const char* DefaultMaterial = "sys:projector.material"; __ImplementClass(App::ProjectorRenderComponent, 'PCOM', App::RenderComponent); ProjectorRenderComponent::ProjectorRenderComponent() : mProjector(NULL), mIsNeedUpdate(false), mVertexLimit(4*1024), mPrimitive(NULL), mTerrainPrimitive(NULL) #ifdef __GENESIS_EDITOR__ // edtior use ,mIsBrushHelper(false) #endif { mProjector = Graphic::Projector::Create(); mAabb.begin_extend(); } //-------------------------------------------------------------------------------- ProjectorRenderComponent::~ProjectorRenderComponent() { if (mProjector.isvalid()) { mProjector = NULL; } } //-------------------------------------------------------------------------------- void ProjectorRenderComponent::SetupCallbacks() { mActor->RegisterComponentCallback(this, BeginFrame); mActor->RegisterComponentCallback(this, OnFrame); mActor->RegisterComponentCallback(this, MoveAfter); } //-------------------------------------------------------------------------------- void ProjectorRenderComponent::SetupAcceptedMessages() { Super::SetupAcceptedMessages(); } //-------------------------------------------------------------------------------- void ProjectorRenderComponent::HandleMessage(const GPtr& msg) { Super::HandleMessage(msg); } //-------------------------------------------------------------------------------- void ProjectorRenderComponent::OnActivate() { Super::OnActivate(); if ( mActor ) { mProjector->SetTransform( mActor->GetWorldTranslateRotation() ); } if ( IsActive() ) { _BuildRenderObject(); _AttachRenderObject(); } } //-------------------------------------------------------------------------------- void ProjectorRenderComponent::OnDeactivate() { Super::OnDeactivate(); if ( !IsActive()) { _DeattachRenderObject(); } if ( mPrimitive.IsValid() ) { Graphic::GraphicSystem::Instance()->RemovePrimitive(mPrimitive); mPrimitive = RenderBase::PrimitiveHandle(); } if (mTerrainPrimitive) { Graphic::GraphicSystem::Instance()->RemovePrimitive(mTerrainPrimitive); mTerrainPrimitive = RenderBase::PrimitiveHandle(); } } void ProjectorRenderComponent::UpdateRenderLayer() { if (mProjectorRenderObject.isvalid()) { mProjectorRenderObject->SetLayerID(mActor->GetLayerID()); } } //-------------------------------------------------------------------------------- void ProjectorRenderComponent::_OnBeginFrame() { Super::_OnBeginFrame(); } //-------------------------------------------------------------------------------- void ProjectorRenderComponent::_OnFrame() { #ifdef __GENESIS_EDITOR__ if(!mIsBrushHelper) { #endif Update(); #ifdef __GENESIS_EDITOR__ } #endif } //-------------------------------------------------------------------------------- void ProjectorRenderComponent::Update() { Super::_OnFrame(); if (mProjector.isvalid() && mIsNeedUpdate && m_bAllTexLoaded) { _UpdateProjector(); _BuildMeshInfo(); _BuildRenderable(); } } //-------------------------------------------------------------------------------- void ProjectorRenderComponent::_UpdateProjector() { // Rebuild mesh vertex container mVertexs.Clear(false); // Rebuild terrain vertex & indices container mTerrainVertexs.Clear(false); mTerrainIndices.Clear(false); // Update shader param & retrieve render objects mProjector->_UpdateProjector(); for(int matIndex=0;matIndex instance = GetMaterial(matIndex); instance->SetSort(Graphic::MatSort_ProjectorBegin+matIndex); } // Collision dealing Math::matrix44 viewProjMatrix = Math::matrix44::zero(); Graphic::CollisionModelServer* collisionModelServer = Graphic::CollisionModelServer::Instance(); // TODO: Optimize usage of the following variables Opcode::PlanesCollider planesCollider; Opcode::PlanesCache planesCache; IceMaths::Plane clipPlanes[6]; const Util::Array >& renderObjs = mProjector->GetRenderObjs(); for (int i = 0; i < renderObjs.Size(); ++i) { // check ignore layer if ( (1 << renderObjs[i]->GetLayerID() & mProjector->GetIgnoreLayers()) || !renderObjs[i]->GetProjected()) { continue; } if ( renderObjs[i] && renderObjs[i]->GetRtti()->IsDerivedFrom(MeshRenderObject::RTTI) && !renderObjs[i]->GetRtti()->IsDerivedFrom(SkinnedRenderObject::RTTI) #ifdef __GENESIS_EDITOR__ && !mIsBrushHelper #endif ) { GPtr renderObj = renderObjs[i]; // skip the subRenderObject such as axis_X etc if ( renderObj->GetLayerID() == eSL_Assist ) continue; GPtr meshRenderObj = renderObj.downcast(); n_assert(meshRenderObj); // Get world matrix const Math::matrix44& worldMatrix = meshRenderObj->GetTransform(); // Compute world-view-projection matrix if ( viewProjMatrix == Math::matrix44::zero() ) { viewProjMatrix = Math::matrix44::multiply( mProjector->_CalcProjMatrix(),Math::matrix44::inverse(mProjector->GetTransform())); } Math::matrix44 worldViewProjMatrix = Math::matrix44::multiply(viewProjMatrix,worldMatrix); // Get the collision mode Util::StringAtom mesh = meshRenderObj->GetMeshName(); const GPtr& collisionModel = collisionModelServer->GetCollisionModel( mesh.AsString() ); // Setup clip planes in model space, NB: Opcode use negative side as inside // formula overturn : \\10.6.34.129\linuxsir\ÄÚѵÎĵµ\ÒýÇæ\Graphics\Math\plane_extraction.pdf clipPlanes[FRUSTUM_PLANE_LEFT].Set( - worldViewProjMatrix[0][0] - worldViewProjMatrix[3][0], - worldViewProjMatrix[0][1] - worldViewProjMatrix[3][1], - worldViewProjMatrix[0][2] - worldViewProjMatrix[3][2], - worldViewProjMatrix[0][3] - worldViewProjMatrix[3][3]); clipPlanes[FRUSTUM_PLANE_RIGHT].Set( + worldViewProjMatrix[0][0] - worldViewProjMatrix[3][0], + worldViewProjMatrix[0][1] - worldViewProjMatrix[3][1], + worldViewProjMatrix[0][2] - worldViewProjMatrix[3][2], + worldViewProjMatrix[0][3] - worldViewProjMatrix[3][3]); clipPlanes[FRUSTUM_PLANE_BOTTOM].Set( - worldViewProjMatrix[1][0] - worldViewProjMatrix[3][0], - worldViewProjMatrix[1][1] - worldViewProjMatrix[3][1], - worldViewProjMatrix[1][2] - worldViewProjMatrix[3][2], - worldViewProjMatrix[1][3] - worldViewProjMatrix[3][3]); clipPlanes[FRUSTUM_PLANE_TOP].Set( + worldViewProjMatrix[1][0] - worldViewProjMatrix[3][0], + worldViewProjMatrix[1][1] - worldViewProjMatrix[3][1], + worldViewProjMatrix[1][2] - worldViewProjMatrix[3][2], + worldViewProjMatrix[1][3] - worldViewProjMatrix[3][3]); clipPlanes[FRUSTUM_PLANE_NEAR].Set( - worldViewProjMatrix[2][0] - worldViewProjMatrix[3][0], - worldViewProjMatrix[2][1] - worldViewProjMatrix[3][1], - worldViewProjMatrix[2][2] - worldViewProjMatrix[3][2], - worldViewProjMatrix[2][3] - worldViewProjMatrix[3][3]); clipPlanes[FRUSTUM_PLANE_FAR].Set( + worldViewProjMatrix[2][0] - worldViewProjMatrix[3][0], + worldViewProjMatrix[2][1] - worldViewProjMatrix[3][1], + worldViewProjMatrix[2][2] - worldViewProjMatrix[3][2], + worldViewProjMatrix[2][3] - worldViewProjMatrix[3][3]); if (!planesCollider.Collide( planesCache, clipPlanes, 6, collisionModel->GetOpCodeModel(), NULL)) continue; const uint* triangles = planesCollider.GetTouchedPrimitives(); const uint numTriangles = planesCollider.GetNbTouchedPrimitives(); if (!triangles || !numTriangles) continue; // Get the view vector base on projection type Math::float4 vv; Math::matrix44 eyeToModel = Math::matrix44::inverse(Math::matrix44::multiply( Math::matrix44::inverse(mProjector->GetTransform()), worldMatrix)); if ( mProjector->GetViewType() == Graphic::Projector::VT_Persp ) { // Use view position as view vector vv[0] = eyeToModel[0][3]; vv[1] = eyeToModel[1][3]; vv[2] = eyeToModel[2][3]; vv[3] = eyeToModel[3][3]; } else { // Use view direction as view vector vv[0] = eyeToModel[0][2]; vv[1] = eyeToModel[1][2]; vv[2] = eyeToModel[2][2]; vv[3] = eyeToModel[3][2]; } const Util::Array& faceNormals = collisionModel->GetFaceNormals(); const Util::Array& vertices = collisionModel->GetVertices(); const Util::Array& indices = collisionModel->GetIndices(); SizeT vertexStart = mVertexs.Size(); SizeT vertexCount = 0; SizeT cullCount = 0; // Reserve memory avoid allocate overhead mVertexs.Reserve(vertexStart + numTriangles*3); for (uint i = 0; i < numTriangles; ++i) { uint triangleIndex = triangles[i]; Math::float4 faceNormal = Math::float4::normalize(faceNormals[triangleIndex]); faceNormal.set_w(0.0f); // check the triangle is projector facing if ( ! Math::n_fequal(Math::float4::dot4(faceNormal,vv), 0.0f, 0.001f) ) { Math::float3 tempPos; Math::float4 result; // transform vertex from model space to world space for (int i = 0; i < 3; ++i,++vertexCount) { int index = triangleIndex*3 + i; tempPos = vertices[indices[index]]; result = Math::matrix44::transform(worldMatrix,Math::float4(tempPos.x(),tempPos.y(),tempPos.z(),1.0f)); tempPos.x() = result.x(); tempPos.y() = result.y(); tempPos.z() = result.z(); mVertexs.Append(tempPos); _UpdateBoundingBox(tempPos); } } } // Check vertex limit n_assert(vertexStart <= mVertexLimit); if ( mVertexLimit <= vertexStart ) break; // Just for safely // check overrun bool overrun = vertexStart + vertexCount > mVertexLimit; if (overrun) { // discard overrun vertices vertexCount = (mVertexLimit - vertexStart)/ 3 * 3; mVertexs.Resize(vertexStart + vertexCount,Math::float3(0.0, 0.0, 0.0)); } // No more process because vertices overrun if (overrun) break; } else if ( renderObjs[i]->GetRtti()->IsDerivedFrom(TerrainRenderObject::RTTI)) { GPtr terrainRenderObject = renderObjs[i].downcast(); n_assert(terrainRenderObject.isvalid()); // catch indices Resources::Index16Container::value_type* pIndices = terrainRenderObject->GetTerrainNodeIndexData(); SizeT indicesCount = terrainRenderObject->GetTerrainNodeIndexDataCount(); SizeT positionCount = mTerrainVertexs.Size(); for (int i = 0 ; i < indicesCount; ++i) { mTerrainIndices.Append(pIndices[i] + positionCount); } // catch position const Resources::PositionData::Elem* pLocalPosition = terrainRenderObject->GetTerrainNodePosData(); SizeT positionsCount = terrainRenderObject->GetTerrainNodePosCount(); for ( int i = 0; i < positionsCount; ++i) { const Math::float3 worldPostion = pLocalPosition[i].transformPoint(terrainRenderObject->GetTransform()); mTerrainVertexs.Append(worldPostion); _UpdateBoundingBox(worldPostion); } } // end handling each render object } // end foreach render object } //-------------------------------------------------------------------------------- void ProjectorRenderComponent::_UpdateBoundingBox( const Math::float3 &pos) { mAabb.extend(Math::float4(pos.x(),pos.y(),pos.z(),1.0f)); } //-------------------------------------------------------------------------------- void ProjectorRenderComponent::SetProjectorTrans(const Math::matrix44& trans) { if (mProjector.isvalid()) { mProjector->SetTransform(trans); } } //-------------------------------------------------------------------------------- Math::scalar ProjectorRenderComponent::GetAlphaValue(Math::scalar u, Math::scalar v) { if (GetMaterial(0).isvalid()) { GPtr pRes = GetMaterial(0)->GetTextureResInfo(0)->GetRes(); n_assert(pRes.isvalid()); GPtr pImageRes = pRes.downcast(); Math::scalar alpha = pImageRes->GetAlphaValue(u, v); return alpha; } else { return 0.0f; } } //-------------------------------------------------------------------------------- void ProjectorRenderComponent::ActiveProjector(bool enable) { EnableProjectorUpdate(enable); } //-------------------------------------------------------------------------------- const bool ProjectorRenderComponent::IsProjectorEnabled() const { return mIsNeedUpdate; } //-------------------------------------------------------------------------------- void ProjectorRenderComponent::_BuildRenderable() { n_assert( mActor ); _BuildRenderObject(); // Generate and fill renderable if ( mProjectorRenderObject.isvalid() && mPrimitive.IsValid() ) { // Mesh mRenderableResUnitList[0].ResetRenderable(); AppRenderable* renderable = mRenderableResUnitList[0].GetRenderableFast(); renderable->SetRenderInfo( 0, mVertexs.Size(), 0, 0); } if (mProjectorRenderObject.isvalid() && mTerrainPrimitive.IsValid() ) { // Terrain mRenderableResUnitList[1].ResetRenderable(); AppRenderable* renderable = mRenderableResUnitList[1].GetRenderableFast(); renderable->SetRenderInfo( 0, mTerrainVertexs.Size(), 0, mTerrainIndices.Size()); } } //-------------------------------------------------------------------------------- void ProjectorRenderComponent::_BuildRenderObject() { // Generate new render object if ( !mProjectorRenderObject.isvalid() ) { mProjectorRenderObject = ProjectorRenderObject::Create(); mProjectorRenderObject->SetOwner(this); mProjectorRenderObject->SetTransform(Math::matrix44::identity()); } mProjectorRenderObject->SetBoundingBox(mAabb); mAabb.begin_extend(); } //-------------------------------------------------------------------------------- void ProjectorRenderComponent::OnRenderSceneChanged() { if (mProjectorRenderObject.isvalid() && mProjectorRenderObject->Attached()) { mProjectorRenderObject->Attach(mActor->GetRenderScene()); } } //-------------------------------------------------------------------------------- void ProjectorRenderComponent::_AttachRenderObject() { if (mProjectorRenderObject.isvalid()) { n_assert(mActor); Graphic::RenderScene* rs = mActor->GetRenderScene(); mProjector->SetRenderScene(rs); mProjectorRenderObject->Attach(rs); } } //-------------------------------------------------------------------------------- void ProjectorRenderComponent::_DeattachRenderObject() { if ( mProjectorRenderObject.isvalid() ) { mProjector->SetRenderScene(NULL); mProjectorRenderObject->Detach(); } } //-------------------------------------------------------------------------------- void ProjectorRenderComponent::_BuildMeshInfo() { using namespace RenderBase; // Mesh Use if ( mVertexs.Size() ) { Graphic::VertexBufferData2 vbd2; vbd2.GetVertexComponents().Append( RenderBase::VertexComponent(RenderBase::VertexComponent::Position,0, RenderBase::VertexComponent::Float3)); // layout vbd2.Setup(mVertexs.Size(), sizeof(Math::float3), RenderBase::BufferData::Static, PrimitiveTopology::TriangleList, true); vbd2.SetVertices(&mVertexs[0], mVertexs.Size()); if (mPrimitive.IsValid()) { Graphic::GraphicSystem::Instance()->ChangePrimitiveHandle(mPrimitive, &vbd2, NULL); } else { mPrimitive = Graphic::GraphicSystem::Instance()->CreatePrimitiveHandle(&vbd2, NULL); } } else { // No points found Graphic::GraphicSystem::Instance()->RemovePrimitive(mPrimitive); mPrimitive = RenderBase::PrimitiveHandle(); } // Terrain use if ( mTerrainVertexs.Size() && mTerrainIndices.Size() ) { Graphic::VertexBufferData2 vbd2; vbd2.GetVertexComponents().Append( RenderBase::VertexComponent(RenderBase::VertexComponent::Position,0, RenderBase::VertexComponent::Float3)); // layout vbd2.Setup(mTerrainVertexs.Size(), sizeof(Math::float3), RenderBase::BufferData::Static, PrimitiveTopology::TriangleList, true); vbd2.SetVertices(&mTerrainVertexs[0], mTerrainVertexs.Size()); Graphic::IndexBufferData2 ibd2; ibd2.Setup(mTerrainIndices.Size(), RenderBase::BufferData::Static, RenderBase::IndexBufferData::Int16, true); ibd2.SetIndices(&mTerrainIndices[0], mTerrainIndices.Size()); if (mTerrainPrimitive.IsValid()) { Graphic::GraphicSystem::Instance()->ChangePrimitiveHandle(mTerrainPrimitive, &vbd2, &ibd2); } else { mTerrainPrimitive = Graphic::GraphicSystem::Instance()->CreatePrimitiveHandle(&vbd2, &ibd2); } } else { // No terrain found Graphic::GraphicSystem::Instance()->RemovePrimitive(mTerrainPrimitive); mTerrainPrimitive = NULL; } } //-------------------------------------------------------------------------------- const RenderBase::PrimitiveHandle& ProjectorRenderComponent::GetPrimitiveHandleByRenderable( const Graphic::Renderable* renderable ) const { if ( renderable == mRenderableResUnitList[0].GetRenderableFast() ) { return mPrimitive; } else { return mTerrainPrimitive; } } //-------------------------------------------------------------------------------- void ProjectorRenderComponent::EnableProjectorUpdate(bool bEnable) { mIsNeedUpdate = bEnable; if (bEnable) { _AttachRenderObject(); } else { _DeattachRenderObject(); } } //-------------------------------------------------------------------------------- void ProjectorRenderComponent::_OnMoveAfter() { if ( mActor ) { mProjector->SetTransform( mActor->GetWorldTranslateRotation() ); } } //-------------------------------------------------------------------------------- void ProjectorRenderComponent::CopyFrom( const GPtr& pComponent ) { if( !pComponent.isvalid() ) return; if( !pComponent->GetRtti()->IsDerivedFrom( *(this->GetRtti()) ) ) return; GPtr pSource = pComponent.downcast(); // copy component data mIsNeedUpdate = pSource->mIsNeedUpdate; SetMaterialID(0, pSource->GetMaterialID(0), false); // copy projector data GPtr pSourceProjector = pSource->GetProJector(); n_assert( pSource->mProjector.isvalid() ); mProjector->CopyParam(pSourceProjector); } //-------------------------------------------------------------------------------- #ifdef __GENESIS_EDITOR__ // edtior use bool ProjectorRenderComponent::GetBrushState() const { return mIsBrushHelper; } //-------------------------------------------------------------------------------- void ProjectorRenderComponent::SetBrushState( const bool b ) { mIsBrushHelper = b; } #endif //-------------------------------------------------------------------------------- } #endif // __PROJECTOR_COMMIT__