#include "SceneDesignerFrameworkPCH.h" #include #include "MGlobalSetting.h" #include "MFramework.h" #include "MScene.h" #include "Terrain.h" using namespace System::Xml; using namespace Emergent::Gamebryo::SceneDesigner::Framework; //using namespace Emergent::Gamebryo::SceneDesigner::PluginAPI; //---------------------------------------------------------------- MGlobalSetting::MGlobalSetting(void) { m_pkColor = NiNew NiColor(1.0f, 1.0f, 1.0f); m_fDepth = 0.5f; m_fNear = 1.0f; m_fFar = 128.0f; m_pMinorEntity = new ArrayList*[MINOR_ENTITY_LEVEL]; for ( int i = 0; i < MINOR_ENTITY_LEVEL; i++ ) { m_pMinorEntity[i] = new ArrayList(); } m_bPostEffectEnabled = false; m_fArrAdjustValue = new float[AT_MAX]; for(int i = 0; i < AT_MAX;i ++) { m_fArrAdjustValue[i] = 0.0f; } m_bBloomEffectEnabled = false; m_fArrBloomConfig = new float[BT_MAX]; m_fArrBloomConfig[BT_LUM] = 0.068f; m_fArrBloomConfig[BT_SCALE] = 1.5f; m_fWaterCauseSize = 0.05f; m_fWaterCauseXOffset = 0.0f; m_fWaterCauseYOffset = 0.1f; m_fWaterCauseFactor = 1.0f; m_bWaterScene = false; // 地形 ambient 系数初始值 m_fTerrainAmbient = 0.3f; } //---------------------------------------------------------------- MGlobalSetting::~MGlobalSetting(void) { NiDelete m_pkColor; m_pkColor = NULL; for ( int i = 0; i < 2; i++ ) { if ( m_pMinorEntity[i] ) { m_pMinorEntity[i] = NULL; } } m_pMinorEntity = NULL; delete [] m_fArrAdjustValue; delete [] m_fArrBloomConfig; } //---------------------------------------------------------------- // 保存/载入 xml 配置 void MGlobalSetting::SaveToXml(String* strXmlFilename) { const char* pcName = MStringToCharPointer(strXmlFilename); //插入Xml声明 TiXmlDocument* pXmlDoc = new TiXmlDocument(pcName); TiXmlDeclaration* pXmlDec = new TiXmlDeclaration("1.0", "gb2312", "yes"); pXmlDoc->InsertEndChild(*pXmlDec); TiXmlElement* pXmlEle = GetAsXmlElement(false); if(pXmlEle == NULL) { return; } //插入根节点 pXmlDoc->LinkEndChild(pXmlEle); pXmlDoc->SaveFile(pcName); MFreeCharPointer(pcName); // add [5/27/2009 hemeng] // 清理GetAsXmlElement中new的节点 pXmlEle->Clear(); pXmlEle = NULL; } //---------------------------------------------------------------- void MGlobalSetting::LoadFromXml(String* strXmlFilename) { if (!System::IO::File::Exists(strXmlFilename)) { return; } const char* pcName = MStringToCharPointer(strXmlFilename); TiXmlDocument* pXmlDoc = new TiXmlDocument(); pXmlDoc->LoadFile(pcName); TiXmlElement* pElmtRoot = pXmlDoc->RootElement(); TiXmlElement* pElmt = pElmtRoot->FirstChildElement(); while (pElmt != NULL) { const char* pszName = pElmt->Value(); if (strcmp(pszName, "Fog") == 0) { LoadFog(pElmt); } else if (strcmp(pszName, "PostEffect") == 0) { LoadPostEffect(pElmt); } else if(strcmp(pszName, "MinorList") == 0) { LoadMinorList(pElmt); } else if(strcmp(pszName,"Bloom") == 0) { LoadBloomEffect(pElmt); } else if (strcmp(pszName,"AABBScaleInfo") == 0) { LoadAABBScaleInfo(pElmt); } else if (strcmp(pszName, "WaterScene") == 0) { LoadWaterScene(pElmt); } else if (strcmp(pszName, "TerrainParam") == 0) { LoadTerrainParam(pElmt); } pElmt = pElmt->NextSiblingElement(); } MFreeCharPointer(pcName); ApplyFogToScene(); } //---------------------------------------------------------------- void MGlobalSetting::LoadFog(TiXmlElement* pElmt) { double fR, fG, fB, fDepth, fNear, fFar; pElmt->QueryDoubleAttribute("R", &fR); pElmt->QueryDoubleAttribute("G", &fG); pElmt->QueryDoubleAttribute("B", &fB); pElmt->QueryDoubleAttribute("Depth", &fDepth); pElmt->QueryDoubleAttribute("Near", &fNear); pElmt->QueryDoubleAttribute("Far", &fFar); int iEnabled; pElmt->QueryIntAttribute("Enabled", &iEnabled); m_pkColor->r = (float)fR; m_pkColor->g = (float)fG; m_pkColor->b = (float)fB; m_fDepth = (float)fDepth; m_fNear = (float)fNear; m_fFar = (float)fFar; m_bEnabled = (bool)iEnabled; } //---------------------------------------------------------------- void MGlobalSetting::LoadPostEffect(TiXmlElement* pElmt) { if(pElmt == NULL) { return; } float fBright,fContrast,fHue,fSat; int iEnable; pElmt->QueryFloatAttribute("Bright",&fBright); pElmt->QueryFloatAttribute("Contrast",&fContrast); pElmt->QueryFloatAttribute("Hue",&fHue); pElmt->QueryFloatAttribute("Saturation",&fSat); pElmt->QueryIntAttribute("Enabled",&iEnable); m_fArrAdjustValue[AT_ADJUST_BRIGHT] = fBright; m_fArrAdjustValue[AT_ADJUST_CONTRAST] = fContrast; m_fArrAdjustValue[AT_ADJUST_HUE] = fHue; m_fArrAdjustValue[AT_ADJUST_SAT] = fSat; m_bPostEffectEnabled = (bool)iEnable; } //--------------------------------------------------------------- void MGlobalSetting::LoadBloomEffect(TiXmlElement* pElmt) { if (pElmt == NULL) { return; } float fLum,fScale; int iEnable; pElmt->QueryFloatAttribute("Luminance", &fLum); pElmt->QueryFloatAttribute("Scale", &fScale); pElmt->QueryIntAttribute("Enabled", &iEnable); m_fArrBloomConfig[BT_LUM] = fLum; m_fArrBloomConfig[BT_SCALE] = fScale; m_bBloomEffectEnabled = (bool)iEnable; } //--------------------------------------------------------------- void MGlobalSetting::LoadTerrainParam(TiXmlElement* pElmt) { if (pElmt == NULL) { return; } float fAmbient; pElmt->QueryFloatAttribute("Ambient", &fAmbient); m_fTerrainAmbient = fAmbient; } //---------------------------------------------------------------- void MGlobalSetting::LoadWaterScene(TiXmlElement* pElmt) { if (pElmt == NULL) { return; } float fTmp; int iEnable; if(pElmt->QueryFloatAttribute("CauseSize",&fTmp) == TIXML_SUCCESS) m_fWaterCauseSize = fTmp; if(pElmt->QueryFloatAttribute("CauseXOffset", &fTmp) == TIXML_SUCCESS) m_fWaterCauseXOffset = fTmp; if(pElmt->QueryFloatAttribute("CauseYOffset", &fTmp) == TIXML_SUCCESS) m_fWaterCauseYOffset = fTmp; if(pElmt->QueryFloatAttribute("CauseFactor", &fTmp) == TIXML_SUCCESS) m_fWaterCauseFactor = fTmp; if(pElmt->QueryIntAttribute("Enabled", &iEnable) == TIXML_SUCCESS) m_bWaterScene = (iEnable == 1); } //---------------------------------------------------------------- void MGlobalSetting::LoadMinorList(TiXmlElement* pElmt) { for ( int i = 0 ; i < MINOR_ENTITY_LEVEL; i++ ) { m_pMinorEntity[i]->Clear(); } TiXmlElement* pElmtEntity = pElmt->FirstChildElement(); while (pElmtEntity != NULL) { const char* pszName = pElmtEntity->Attribute("Name"); int iLevel; //如果读取的是旧版配置文件则初始化iLevel为1(中等等级) if ( pElmtEntity->QueryIntAttribute( "Level", &iLevel ) == TIXML_NO_ATTRIBUTE) { iLevel = 1; } if ( iLevel > MINOR_ENTITY_LEVEL ) { NiMessageBox( "GlobalSetting中次要物品等级超出设定范围", "Error" ); return; } m_pMinorEntity[iLevel-1]->Add( new String(pszName) ); pElmtEntity = pElmtEntity->NextSiblingElement(); } } //---------------------------------------------------------------- void MGlobalSetting::LoadAABBScaleInfo(TiXmlElement* pElmt) { TiXmlElement* pElmtChild = pElmt->FirstChildElement(); while (pElmtChild != NULL) { const char* pszName = pElmtChild->Attribute("EntityName"); float fScale; pElmtChild->QueryFloatAttribute("Scale",&fScale); CBoundGeometroy::GetInstance()->SetBoxScale(pszName,fScale); pElmtChild = pElmtChild->NextSiblingElement(); } } //---------------------------------------------------------------- TiXmlElement* MGlobalSetting::GetFogAsXmlElement() { TiXmlElement* pElmtFog = new TiXmlElement("Fog"); pElmtFog->SetDoubleAttribute("R", m_pkColor->r); pElmtFog->SetDoubleAttribute("G", m_pkColor->g); pElmtFog->SetDoubleAttribute("B", m_pkColor->b); pElmtFog->SetDoubleAttribute("Depth", m_fDepth); pElmtFog->SetAttribute("Enabled", (int)m_bEnabled); pElmtFog->SetDoubleAttribute("Near", m_fNear); pElmtFog->SetDoubleAttribute("Far", m_fFar); return pElmtFog; } //------------------------------------------------------------------ TiXmlElement* MGlobalSetting::GetAABBScale() { TiXmlElement* pElmtAABB = new TiXmlElement("AABBScaleInfo"); map mapTmp = CBoundGeometroy::GetInstance()->GetScaleInfo(); map::iterator it = mapTmp.begin(); for (; it != mapTmp.end(); it++) { TiXmlElement *pElmtChild = new TiXmlElement( "ScaleInfo" ); const char* pszEntityName = it->first.c_str(); pElmtChild->SetAttribute( "EntityName", pszEntityName ); pElmtChild->SetDoubleAttribute( "Scale", it->second ); pElmtAABB->LinkEndChild(pElmtChild); } return pElmtAABB; } //---------------------------------------------------------------- TiXmlElement* MGlobalSetting::GetPostEffectAsXmlElement() { TiXmlElement *pElmtPostEffect = new TiXmlElement("PostEffect"); pElmtPostEffect->SetDoubleAttribute("Bright",m_fArrAdjustValue[AT_ADJUST_BRIGHT]); pElmtPostEffect->SetDoubleAttribute("Contrast",m_fArrAdjustValue[AT_ADJUST_CONTRAST]); pElmtPostEffect->SetDoubleAttribute("Hue",m_fArrAdjustValue[AT_ADJUST_HUE]); pElmtPostEffect->SetDoubleAttribute("Saturation",m_fArrAdjustValue[AT_ADJUST_SAT]); pElmtPostEffect->SetAttribute("Enabled",(int)m_bPostEffectEnabled); return pElmtPostEffect; } // add [5/27/2009 hemeng] TiXmlElement* MGlobalSetting::GetBloomAsXmlElement() { TiXmlElement* pElmtPostEffect = new TiXmlElement("Bloom"); pElmtPostEffect->SetDoubleAttribute("Luminance", m_fArrBloomConfig[BT_LUM]); pElmtPostEffect->SetDoubleAttribute("Scale", m_fArrBloomConfig[BT_SCALE]); pElmtPostEffect->SetAttribute("Enabled", (int)m_bBloomEffectEnabled); return pElmtPostEffect; } //---------------------------------------------------------------- TiXmlElement* MGlobalSetting::GetWaterSceneAsXmlElement() { TiXmlElement* pElmtWaterScene = new TiXmlElement("WaterScene"); pElmtWaterScene->SetDoubleAttribute("CauseSize", m_fWaterCauseSize); pElmtWaterScene->SetDoubleAttribute("CauseXOffset", m_fWaterCauseXOffset); pElmtWaterScene->SetDoubleAttribute("CauseYOffset", m_fWaterCauseYOffset); pElmtWaterScene->SetDoubleAttribute("CauseFactor", m_fWaterCauseFactor); pElmtWaterScene->SetAttribute("Enabled", (int)m_bWaterScene); return pElmtWaterScene; } //---------------------------------------------------------------- TiXmlElement* MGlobalSetting::GetMinorListAsXmlElement() { TiXmlElement *pElmtMinorList = new TiXmlElement("MinorList"); for ( int i = 0; i < MINOR_ENTITY_LEVEL; i++ ) { ArrayList* pMinorEntities = m_pMinorEntity[i]; for ( int j = 0; j < pMinorEntities->Count; j++ ) { String* strEntity = dynamic_cast( (pMinorEntities->ToArray())[j] ); const char* pcName = MStringToCharPointer( strEntity ); TiXmlElement *pElmtMinorEntity = new TiXmlElement( "MinorEntity" ); pElmtMinorEntity->SetAttribute( "Name", pcName ); MFreeCharPointer( pcName ); pElmtMinorEntity->SetAttribute( "Level", i+1 ); pElmtMinorList->LinkEndChild(pElmtMinorEntity); } } return pElmtMinorList; } //---------------------------------------------------------------- TiXmlElement* MGlobalSetting::GetTerrainParamAsXmlElement() { TiXmlElement* pElmtTerrainParam = new TiXmlElement("TerrainParam"); pElmtTerrainParam->SetDoubleAttribute("Ambient", m_fTerrainAmbient); return pElmtTerrainParam; } //---------------------------------------------------------------- TiXmlElement* MGlobalSetting::GetAsXmlElement(bool bExprot) { //插入根节点 // modified [5/18/2009 hemeng] // 修改数据以便xml导出为dll TiXmlElement* pXmlRootElement = new TiXmlElement("GlobalSetting"); pXmlRootElement->SetAttribute("CheckLines","false"); pXmlRootElement->LinkEndChild(GetFogAsXmlElement()); pXmlRootElement->LinkEndChild(GetPostEffectAsXmlElement()); // add [5/27/2009 hemeng] // bloom pXmlRootElement->LinkEndChild(GetBloomAsXmlElement()); // water scene [10/26/2009 hemeng] pXmlRootElement->LinkEndChild(GetWaterSceneAsXmlElement()); // 包围盒缩放信息 [9/15/2009 hemeng] pXmlRootElement->LinkEndChild(GetAABBScale()); // 如果不是导出则需要保存次要物品列表 [6/25/2009 hemeng] if (!bExprot) { pXmlRootElement->LinkEndChild(GetMinorListAsXmlElement()); } // 地形参数信息 pXmlRootElement->LinkEndChild(GetTerrainParamAsXmlElement()); return pXmlRootElement; } //---------------------------------------------------------------- void MGlobalSetting::Clear() { m_pkColor->r = 1.0f; m_pkColor->g = 1.0f; m_pkColor->b = 1.0f; m_fDepth = 1.0f; m_fNear = 1.0f; m_fFar = 128.0f; m_bEnabled = false; ClearMinorEntityList(); } //---------------------------------------------------------------- // 为一个 node 添加 fog property void MGlobalSetting::AttachFogProperty(NiAVObject* pkAVObj) { if (pkAVObj == NULL) { return; } // 判断 extra data 中是否有 zMode10 NiExtraData* pkExtraData = pkAVObj->GetExtraData("UserPropBuffer"); if (pkExtraData != NULL) { NiStringExtraData* pkStrData = NiStaticCast(NiStringExtraData, pkExtraData); if (pkStrData != NULL) { const NiFixedString& strValue = pkStrData->GetValue(); if (strValue.Contains("zMode10")) { return; } } } if (NiIsKindOf(NiBillboardNode, pkAVObj)) { // 忽略 billboard return; } if (NiIsKindOf(NiParticles, pkAVObj)) { // 忽略粒子 return; } if (NiIsKindOf(NiNode, pkAVObj)) { // 遍历所有子节点 NiNode* pkNode = NiDynamicCast(NiNode, pkAVObj); for (unsigned int i=0; iGetChildCount(); i++) { NiAVObject* pkChild = pkNode->GetAt(i); AttachFogProperty(pkChild); } } if (NiIsKindOf(NiGeometry, pkAVObj)) { NiFogProperty* pkFogProp = (NiFogProperty*)(pkAVObj->GetProperty(NiProperty::FOG)); if (pkFogProp == NULL) { pkFogProp = NiNew NiFogProperty(); pkAVObj->AttachProperty(pkFogProp); pkAVObj->UpdateEffects(); pkAVObj->UpdateProperties(); } pkFogProp->SetFog(m_bEnabled); pkFogProp->SetDepth(m_fDepth); pkFogProp->SetFogColor(*m_pkColor); } } //--------------------------------------------------------------------------- // utility functor class MAIN_ENTRY RecursiveFindAlphaBlend { public: RecursiveFindAlphaBlend() { m_bAlphaBlend = false; } bool operator() (NiAVObject* pkAVObject) { NiAlphaProperty* pAlphaProp = (NiAlphaProperty*)(pkAVObject->GetProperty(NiProperty::ALPHA)); if (pAlphaProp!=NULL && pAlphaProp->GetAlphaBlending()) { m_bAlphaBlend = true; } return true; } bool HasAlphaBlend() {return m_bAlphaBlend;} protected: bool m_bAlphaBlend; // 是否具有 alpha blend 属性 }; //---------------------------------------------------------------- // 添加 fog property void MGlobalSetting::AttachFogProperty(MEntity* pmEntity) { NiAVObject* pkAVObj = pmEntity->GetSceneRootPointer(0); //NiNode* pkNode = (NiNode*)pkAVObj; AttachFogProperty(pkAVObj); } //---------------------------------------------------------------- // 添加 fog property void MGlobalSetting::AttachFogProperty(MEntity* amEntity[]) { for (int i=0; iCount; i++) { AttachFogProperty(amEntity[i]); } } //---------------------------------------------------------------- // 设置深度 void MGlobalSetting::SetFogDepth(float fDepth) { m_fDepth = fDepth; } //---------------------------------------------------------------- // 设置颜色 void MGlobalSetting::SetFogColor(NiColor& kColor) { (*m_pkColor) = kColor; } //---------------------------------------------------------------- // 设置是否开启雾效 void MGlobalSetting::SetFogEnabled(bool bEnabled) { m_bEnabled = bEnabled; } //---------------------------------------------------------------- // 设置场景所有物件雾效属性 void MGlobalSetting::SetFogProperties(System::Drawing::Color* color, float fDepth, bool bEnabled) { m_fDepth = fDepth; (*m_pkColor) = NiColor(color->R/255.0f, color->G/255.0f, color->B/255.0f); m_bEnabled = bEnabled; } //---------------------------------------------------------------- // 设置远近平面 void MGlobalSetting::SetNearFarPlane(float fNear, float fFar) { m_fNear = fNear; m_fFar = fFar; } //---------------------------------------------------------------- void MGlobalSetting::ApplyFogToScene() { if (MFramework::Instance->Scene->Terrain == NULL) { return; } MEntity* amEntity[] = MFramework::Instance->Scene->GetEntities(); AttachFogProperty(amEntity); float arrFogParam[4] = {m_bEnabled?1.0f:0.0f, m_fNear, m_fFar, m_fDepth}; // 是否开启, 近平面, 远平面, 深度 NiShaderFactory::UpdateGlobalShaderConstant("g_vFogParam", sizeof( arrFogParam), arrFogParam); NiShaderFactory::UpdateGlobalShaderConstant("g_vFogColor", sizeof(NiColor), m_pkColor); // 不再给地形添加 fog property, sm3.0 中 fog 效果需要自己在 shader 中实现 //AttachFogProperty(MFramework::Instance->Scene->Terrain->GetTerrainRootNode()); } //---------------------------------------------------------------- // 添加/删除名称到次要物件列表 void MGlobalSetting::AddMinorEntity(System::String* strEntityName, int iMinorLevel) { if ( !m_pMinorEntity[iMinorLevel]->Contains( strEntityName ) ) { m_pMinorEntity[iMinorLevel]->Add(strEntityName); } } //---------------------------------------------------------------- void MGlobalSetting::RemoveMinorEntity(System::String* strEntityName, int iMinorLevel) { if ( m_pMinorEntity[iMinorLevel]->Contains( strEntityName ) ) { m_pMinorEntity[iMinorLevel]->Remove(strEntityName); } } //---------------------------------------------------------------- // 清空次要物件列表 void MGlobalSetting::ClearMinorEntityList() { if ( m_pMinorEntity ) { for ( int i = 0; i < MINOR_ENTITY_LEVEL; i++ ) { m_pMinorEntity[i]->Clear(); } } } //---------------------------------------------------------------- // 获取唯一实例 MGlobalSetting* MGlobalSetting::get_Instance() { if (ms_pmThis == NULL) { ms_pmThis = new MGlobalSetting(); } return ms_pmThis; } //---------------------------------------------------------------- float MGlobalSetting::get_NearPlane() { return m_fNear; } //---------------------------------------------------------------- float MGlobalSetting::get_FarPlane() { return m_fFar; } //---------------------------------------------------------------- bool MGlobalSetting::get_FogEnabled() { return m_bEnabled; } //---------------------------------------------------------------- float MGlobalSetting::get_Depth() { return m_fDepth; } //---------------------------------------------------------------- int MGlobalSetting::get_R() { return (int)(m_pkColor->r*255); } //---------------------------------------------------------------- int MGlobalSetting::get_G() { return (int)(m_pkColor->g*255); } //---------------------------------------------------------------- int MGlobalSetting::get_B() { return (int)(m_pkColor->b*255); } //---------------------------------------------------------------- ArrayList* MGlobalSetting::get_MinorEntityList()[] { return m_pMinorEntity; } // add [5/27/2009 hemeng] float MGlobalSetting::get_Bloom_Lum() { return m_fArrBloomConfig[BT_LUM]; } //------------------------------------------------------------------ float MGlobalSetting::get_Bloom_Scale() { return m_fArrBloomConfig[BT_SCALE]; } //---------------------------------------------------------------- void MGlobalSetting::Shutdown() { delete ms_pmThis; } //---------------------------------------------------------------- ////////////////////////////////////////////////////////////////////////// //080905 add by 和萌 post effect 相关 void MGlobalSetting::SetColorAdjustEffectEnabled(bool bEnabled) { m_bPostEffectEnabled = bEnabled; } bool MGlobalSetting::GetColorAdjustEffectEnabled() { return m_bPostEffectEnabled; } void MGlobalSetting::SetAdjustValueByType(EAdjustType type,float value) { m_fArrAdjustValue[type] = value; } float MGlobalSetting::GetAdjustValueByType(EAdjustType type) { return m_fArrAdjustValue[type]; } // add [5/27/2009 hemeng] void MGlobalSetting::SetBloomEffectEnabled(bool bEnabled) { m_bBloomEffectEnabled = bEnabled; } bool MGlobalSetting::GetBloomEffectEnabled() { return m_bBloomEffectEnabled; } void MGlobalSetting::SetBloomValueByType(EBloomType type, float value) { m_fArrBloomConfig[type] = value; } float MGlobalSetting::GetBloomValueByType(EBloomType type) { return m_fArrBloomConfig[type]; } void MGlobalSetting::SetWaterSceneEnabled(bool bEnabled) { m_bWaterScene = bEnabled; } bool MGlobalSetting::GetWaterSceneEnabled() { return m_bWaterScene; } void MGlobalSetting::SetWaterSceneCauseSize(float fSzie) { m_fWaterCauseSize = fSzie; } float MGlobalSetting::GetWaterSceneCauseSize() { return m_fWaterCauseSize; } void MGlobalSetting::SetWaterSceneCauseXOffset(float fXOffset) { m_fWaterCauseXOffset = fXOffset; } float MGlobalSetting::GetWaterSceneCauseXOffset() { return m_fWaterCauseXOffset; } void MGlobalSetting::SetWaterSceneCauseYOffset(float fYOffset) { m_fWaterCauseYOffset = fYOffset; } float MGlobalSetting::GetWaterSceneCauseYOffset() { return m_fWaterCauseYOffset; } void MGlobalSetting::SetWaterSceneCauseFactor(float fFactor) { m_fWaterCauseFactor = fFactor; } float MGlobalSetting::GetWaterSceneCauseFactor() { return m_fWaterCauseFactor; } //--------------------------------------------------------------------- // 地形的 ambient 光影响系数 float MGlobalSetting::get_TerrainAmbient() { return m_fTerrainAmbient; } //--------------------------------------------------------------------- void MGlobalSetting::set_TerrainAmbient(float fAmbient) { m_fTerrainAmbient = fAmbient; } // end add [11/16/2009 hemeng]