//------------------------------------------------------------------------------
// Lamp : Open source game middleware
// Copyright (C) 2004  Junpei Ohtani ( Email : junpee@users.sourceforge.jp )
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//------------------------------------------------------------------------------

/** @file
 * シーン実装
 * @author Junpee
 */

#include "LampBasic.h"
#include "Graphics/Scene/Scene.h"
#include "Core/Renamer/CountRenamer.h"
#include "Graphics/Camera/CameraManager.h"
#include "Graphics/Fog/Fog.h"
#include "Graphics/SceneNode/SceneNodeManager.h"
#include "Graphics/Light/LightManager.h"
#include "Graphics/Model/ModelManager.h"
#include "Graphics/Mesh/MeshManager.h"
#include "Graphics/MeshData/MeshDataManager.h"
#include "Graphics/Material/MaterialManager.h"
#include "Graphics/Texture/TextureManager.h"
#include "Graphics/Picture/PictureManager.h"
#include "Graphics/Renderer/DrawRequest.h"

namespace Lamp{

//------------------------------------------------------------------------------
// コンストラクタ
Scene::Scene(const String& name) :
	name_(name), currentCamera_(NULL), tick_(0){
	// リネーマの初期化
	renamer_ = new CountRenamer();
	// 各マネージャ初期化
	cameraManager_ = new CameraManager(this);
	sceneNodeManager_ = new SceneNodeManager(this);
	lightManager_ = new LightManager(this);
	modelManager_ = new ModelManager(this);
	meshManager_ = new MeshManager(this);
	meshDataManager_ = new MeshDataManager(this);
	materialManager_ = new MaterialManager(this);
	textureManager_ = new TextureManager(this);
	pictureManager_ = new PictureManager(this);
	// ルートノード初期化
	rootNode_ = sceneNodeManager_->createSceneNode("RootNode");
	// フォグ初期化
	fog_ = new Fog();
}
//------------------------------------------------------------------------------
// デストラクタ
Scene::~Scene(){
	// フォグの後始末
	SafeDelete(fog_);
	// ルートノードの後始末
	sceneNodeManager_->destroy(rootNode_);
	// 各マネージャの後始末
	SafeDelete(pictureManager_);
	SafeDelete(textureManager_);
	SafeDelete(materialManager_);
	SafeDelete(meshDataManager_);
	SafeDelete(meshManager_);
	SafeDelete(modelManager_);
	SafeDelete(lightManager_);
	SafeDelete(sceneNodeManager_);
	SafeDelete(cameraManager_);
	// リネーマの後始末
	SafeDelete(renamer_);
}
//------------------------------------------------------------------------------
// クリア
int Scene::clear(){
	int result = 0;
	result += pictureManager_->clear();
	result += textureManager_->clear();
	result += materialManager_->clear();
	result += meshDataManager_->clear();
	result += meshManager_->clear();
	// RootNodeを残すため、SceneLeafよりも先にSceneNodeをクリアする必要がある
	result += sceneNodeManager_->clear();
	result += modelManager_->clear();
	result += lightManager_->clear();
	result += cameraManager_->clear();
	return result;
}
//------------------------------------------------------------------------------
// 走査
void Scene::traverse(){
	// チックのインクリメント
	tick_++;
	rootNode_->traverse();
}
//------------------------------------------------------------------------------
// メッシュリストの取得
void Scene::getMeshList(ArrayList<Mesh*>* meshList, Camera* camera){
	// シーン管理システムによる高速な刈り込みに動的に切り替えれるようにする
	int meshCount = meshManager_->getCount();
	for(int i = 0; i < meshCount; i++){
		Mesh* mesh = meshManager_->get(i);
		if(!mesh->isGlobalEnabled()){ continue; }
		// マテリアル、メッシュデータを持っていないメッシュは禁止
		Assert(mesh->getMaterial() != NULL);
		Assert(mesh->getMeshData() != NULL);
		// スフィアチェックにより見えないメッシュをはじく
		Clipping::State clippingState =
			camera->clipping(mesh->getWorldBoundingSphere());
		if(clippingState == Clipping::invisible){ continue; }
		// 交差している場合はボックスチェックも行う
		if(clippingState == Clipping::intersect){
			clippingState = camera->clipping(mesh->getWorldBoundingBox());
			if(clippingState == Clipping::invisible){ continue; }
		}
		// リストに追加
		meshList->add(mesh);
	}
}
//------------------------------------------------------------------------------
// ローカルライトリストの取得
void Scene::getLocalLightList(Mesh* mesh, DrawRequest* drawRequest){
	// シーン管理システムによる高速な刈り込みに動的に切り替えれるようにする
	const Sphere& meshSphere = mesh->getWorldBoundingSphere();
	const AxisAlignedBox& meshBox = mesh->getWorldBoundingBox();
	u_int lightMask = drawRequest->getMaterial()->getLightMask();
	int lightCount = lightManager_->getCount();
	for(int i = 0; i < lightCount; i++){
		Light* light = lightManager_->get(i);
		if(!light->isLocalLight()){ continue; }
		if(!light->isGlobalEnabled()){ continue; }
		// ライトマスク判定
		if((light->getLightMask() & lightMask) == 0){ continue; }
		if(light->isPointLight()){
			// ポイントライト
			PointLight* pointLight = light->castPointLight();
			// スフィアチェック
			float limitSquaredDistance =
				pointLight->getRange() + meshSphere.getRadius();
			limitSquaredDistance *= limitSquaredDistance;
			float checkValue = limitSquaredDistance -
				(pointLight->getWorldPosition() -
				meshSphere.getCenter()).getSquaredLength();
			if(checkValue <= 0.f){ continue; }
			// ボックスチェック
			Sphere lightSphere(
				pointLight->getWorldPosition(), pointLight->getRange());
			if(!meshBox.intersect(lightSphere)){ continue; }
			// 描画リクエストに追加
			drawRequest->addLocalLight(pointLight);
		}else{
			ErrorOut("Scene::getLocalLightList() 非サポートのライトです");
		}
	}
}
//------------------------------------------------------------------------------
// リネーマの設定
void Scene::setRenamer(Renamer* renamer){
	Assert(renamer != NULL);
	SafeDelete(renamer_);
	renamer_ = renamer;
}
//------------------------------------------------------------------------------
// デバイスオブジェクトの初期化
bool Scene::initializeGraphicsDeviceObjects(){
	if(!meshManager_->initializeGraphicsDeviceObjects()){ return false; }
	if(!meshDataManager_->initializeGraphicsDeviceObjects()){ return false; }
	if(!materialManager_->initializeGraphicsDeviceObjects()){ return false; }
	if(!pictureManager_->initializeGraphicsDeviceObjects()){ return false; }
	return true;
}
//------------------------------------------------------------------------------
// デバイスオブジェクトの削除
void Scene::deleteGraphicsDeviceObjects(){
	pictureManager_->deleteGraphicsDeviceObjects();
	materialManager_->deleteGraphicsDeviceObjects();
	meshDataManager_->deleteGraphicsDeviceObjects();
	meshManager_->deleteGraphicsDeviceObjects();
}
//------------------------------------------------------------------------------
// デバイスオブジェクトのリストア
bool Scene::restoreGraphicsDeviceObjects(){
	if(!meshManager_->restoreGraphicsDeviceObjects()){ return false; }
	if(!meshDataManager_->restoreGraphicsDeviceObjects()){ return false; }
	if(!materialManager_->restoreGraphicsDeviceObjects()){ return false; }
	if(!pictureManager_->restoreGraphicsDeviceObjects()){ return false; }
	return true;
}
//------------------------------------------------------------------------------
// デバイスオブジェクトの無効化
void Scene::invalidateGraphicsDeviceObjects(){
	pictureManager_->invalidateGraphicsDeviceObjects();
	materialManager_->invalidateGraphicsDeviceObjects();
	meshDataManager_->invalidateGraphicsDeviceObjects();
	meshManager_->invalidateGraphicsDeviceObjects();
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
