/* This file is part of Caelum. See http://www.ogre3d.org/wiki/index.php/Caelum Copyright (c) 2006-2008 Caelum team. See Contributors.txt for details. Caelum 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 3 of the License, or (at your option) any later version. Caelum 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 Caelum. If not, see . */ #include "CaelumPrecompiled.h" #include "CaelumPrerequisites.h" #if CAELUM_SCRIPT_SUPPORT #include "CaelumScriptTranslator.h" #include "CaelumSystem.h" #include "CaelumExceptions.h" using namespace Ogre; namespace Caelum { PropScriptResource::PropScriptResource ( Ogre::ResourceManager* creator, const Ogre::String& name, Ogre::ResourceHandle handle, const Ogre::String& group, bool isManual, Ogre::ManualResourceLoader* loader ): Ogre::Resource (creator, name, handle, group, isManual, loader) { //Ogre::LogManager::getSingleton().logMessage( // "PropScriptResource::PropScriptResource"); } PropScriptResource::~PropScriptResource() { //Ogre::LogManager::getSingleton().logMessage( // "PropScriptResource::~PropScriptResource"); } PropScriptResourceManager::PropScriptResourceManager() { mLoadOrder = 1000; mResourceType = "PropertyScript"; } PropScriptResource* PropScriptResourceManager::createImpl( const String& name, ResourceHandle handle, const String& group, bool isManual, ManualResourceLoader* loader, const NameValuePairList* createParams) { //Ogre::LogManager::getSingleton().logMessage( // "PropScriptResourceManager::createImpl"); return new PropScriptResource (this, name, handle, group, isManual, loader); } TypeDescriptorScriptTranslator::TypeDescriptorScriptTranslator (TypeDescriptor* typeDescriptor): mTypeDescriptor(typeDescriptor) { } bool TypeDescriptorScriptTranslator::getPropValueOrAddError ( ScriptCompiler* compiler, PropertyAbstractNode* prop, bool& value) { if (prop->values.empty ()) { compiler->addError (ScriptCompiler::CE_STRINGEXPECTED, prop->file, prop->line); return false; } if (prop->values.size () > 1) { compiler->addError (ScriptCompiler::CE_FEWERPARAMETERSEXPECTED, prop->file, prop->line, prop->name + " must have at most 1 argument"); return false; } if (!Ogre::ScriptTranslator::getBoolean(prop->values.front(), &value)) { compiler->addError (ScriptCompiler::CE_INVALIDPARAMETERS, prop->file, prop->line, prop->values.front()->getValue() + " is not a valid number"); return false; } return true; } bool TypeDescriptorScriptTranslator::getPropValueOrAddError ( ScriptCompiler* compiler, PropertyAbstractNode* prop, ColourValue& value) { if (prop->values.empty ()) { compiler->addError (ScriptCompiler::CE_STRINGEXPECTED, prop->file, prop->line); return false; } if (prop->values.size () > 4) { compiler->addError (ScriptCompiler::CE_FEWERPARAMETERSEXPECTED, prop->file, prop->line, prop->name + " must have at most 4 arguments"); return false; } if (prop->values.size () < 3) { compiler->addError (ScriptCompiler::CE_FEWERPARAMETERSEXPECTED, prop->file, prop->line, prop->name + " must have at least 3 arguments"); } if (!getColour(prop->values.begin(), prop->values.end(), &value)) { compiler->addError (ScriptCompiler::CE_INVALIDPARAMETERS, prop->file, prop->line, prop->name + " requires a colour argument"); } return true; } bool TypeDescriptorScriptTranslator::getPropValueOrAddError ( ScriptCompiler* compiler, PropertyAbstractNode* prop, float& value) { if (prop->values.empty ()) { compiler->addError (ScriptCompiler::CE_STRINGEXPECTED, prop->file, prop->line); return false; } if (prop->values.size () > 1) { compiler->addError (ScriptCompiler::CE_FEWERPARAMETERSEXPECTED, prop->file, prop->line, prop->name + " must have at most 1 argument"); return false; } if (!Ogre::ScriptTranslator::getFloat(prop->values.front(), &value)) { compiler->addError (ScriptCompiler::CE_INVALIDPARAMETERS, prop->file, prop->line, prop->values.front()->getValue() + " is not a valid number"); return false; } return true; } bool TypeDescriptorScriptTranslator::getPropValueOrAddError ( ScriptCompiler* compiler, PropertyAbstractNode* prop, int& value) { if (prop->values.empty ()) { compiler->addError (ScriptCompiler::CE_STRINGEXPECTED, prop->file, prop->line); return false; } if (prop->values.size () > 1) { compiler->addError (ScriptCompiler::CE_FEWERPARAMETERSEXPECTED, prop->file, prop->line, prop->name + " must have at most 1 argument"); return false; } if (!Ogre::ScriptTranslator::getInt(prop->values.front(), &value)) { compiler->addError (ScriptCompiler::CE_INVALIDPARAMETERS, prop->file, prop->line, prop->values.front()->getValue() + " is not a valid integer"); return false; } return true; } bool TypeDescriptorScriptTranslator::getPropValueOrAddError ( ScriptCompiler* compiler, PropertyAbstractNode* prop, double& value) { if (prop->values.empty ()) { compiler->addError (ScriptCompiler::CE_STRINGEXPECTED, prop->file, prop->line); return false; } if (prop->values.size () > 1) { compiler->addError (ScriptCompiler::CE_FEWERPARAMETERSEXPECTED, prop->file, prop->line, prop->name + " must have at most 1 argument"); return false; } // We do need a string stream here for the extra precision. std::stringstream strStream (std::string(prop->values.front()->getValue())); strStream >> value; if (strStream.fail()) { compiler->addError (ScriptCompiler::CE_INVALIDPARAMETERS, prop->file, prop->line, prop->values.front()->getValue() + " is not a valid number"); return false; } return true; } bool TypeDescriptorScriptTranslator::getPropValueOrAddError ( ScriptCompiler* compiler, PropertyAbstractNode* prop, Ogre::Degree& value) { if (prop->values.size () == 0) { compiler->addError (ScriptCompiler::CE_STRINGEXPECTED, prop->file, prop->line); return false; } if (prop->values.size () > 3) { compiler->addError (ScriptCompiler::CE_FEWERPARAMETERSEXPECTED, prop->file, prop->line, prop->name + " must have at most 3 arguments"); return false; } // Allow 3 components. float degMinSec[3] = { 0, 0, 0 }; int k = 0; for (AbstractNodeList::const_iterator it = prop->values.begin(), endIt = prop->values.end(); it != endIt; ++it, ++k) { if (!Ogre::ScriptTranslator::getFloat(*it, °MinSec[k])) { compiler->addError (ScriptCompiler::CE_INVALIDPARAMETERS, prop->file, prop->line, (*it)->getValue () + " is not a valid number"); return false; } } value = Ogre::Degree(degMinSec[0] + degMinSec[1] / 60.0 + degMinSec[2] / 3600); return true; } bool TypeDescriptorScriptTranslator::getPropValueOrAddError ( ScriptCompiler* compiler, PropertyAbstractNode* prop, Ogre::String& value) { if (prop->values.size () == 0) { compiler->addError (ScriptCompiler::CE_STRINGEXPECTED, prop->file, prop->line); return false; } if (prop->values.size () > 1) { compiler->addError (ScriptCompiler::CE_FEWERPARAMETERSEXPECTED, prop->file, prop->line, prop->name + " must have at most 1 arguments"); return false; } if (!Ogre::ScriptTranslator::getString(prop->values.front(), &value)) { compiler->addError (ScriptCompiler::CE_INVALIDPARAMETERS, prop->file, prop->line, prop->values.front()->getValue() + " is not a valid string"); return false; } return true; } bool TypeDescriptorScriptTranslator::getPropValueOrAddError ( ScriptCompiler* compiler, PropertyAbstractNode* prop, Ogre::Vector3& value) { if (prop->values.size () == 0) { compiler->addError (ScriptCompiler::CE_STRINGEXPECTED, prop->file, prop->line); return false; } if (prop->values.size () > 3) { compiler->addError (ScriptCompiler::CE_FEWERPARAMETERSEXPECTED, prop->file, prop->line, prop->name + " must have at most 3 arguments"); return false; } float floats[3]; if (!Ogre::ScriptTranslator::getFloats(prop->values.begin(), prop->values.end(), floats, 3)) { compiler->addError (ScriptCompiler::CE_INVALIDPARAMETERS, prop->file, prop->line, "incorrect vector parameters."); return false; } value.x = floats[0]; value.y = floats[1]; value.z = floats[2]; return true; } bool TypeDescriptorScriptTranslator::getPropValueOrAddError ( ScriptCompiler* compiler, PropertyAbstractNode* prop, Ogre::Vector2& value) { if (prop->values.size () == 0) { compiler->addError (ScriptCompiler::CE_STRINGEXPECTED, prop->file, prop->line); return false; } if (prop->values.size () > 2) { compiler->addError (ScriptCompiler::CE_FEWERPARAMETERSEXPECTED, prop->file, prop->line, prop->name + " must have at most 3 arguments"); return false; } float floats[2]; if (!Ogre::ScriptTranslator::getFloats(prop->values.begin(), prop->values.end(), floats, 2)) { compiler->addError (ScriptCompiler::CE_INVALIDPARAMETERS, prop->file, prop->line, "incorrect vector parameters."); return false; } value.x = floats[0]; value.y = floats[1]; return true; } template static bool tryHandlePropertyType ( ScriptCompiler* compiler, PropertyAbstractNode* propNode, void* targetObject, const ValuePropertyDescriptor* propDesc) { if (propDesc->getValueTypeId () == typeid(T)) { T val; if (TypeDescriptorScriptTranslator::getPropValueOrAddError (compiler, propNode, val)) { propDesc->setValue (targetObject, Ogre::Any(val)); } return true; } return false; } void TypeDescriptorScriptTranslator::translateProperty ( ScriptCompiler* compiler, PropertyAbstractNode* prop, void* targetObject, const TypeDescriptor* typeDescriptor) { const ValuePropertyDescriptor* propDesc = typeDescriptor->getPropertyDescriptor (prop->name); if (!propDesc) { compiler->addError (ScriptCompiler::CE_UNEXPECTEDTOKEN, prop->file, prop->line, "property \"" + prop->name + "\" not recognized; missing from type descriptor."); return; } if (!propDesc->canSetValue ()) { compiler->addError (ScriptCompiler::CE_UNEXPECTEDTOKEN, prop->file, prop->line, "property \"" + prop->name + "\" is read-only and can't be set from a script."); return; } //LogManager::getSingleton ().logMessage ("TypeDescriptorScriptTranslator::translateProperty" // " name '" + prop->name + "'"); bool handled = false || tryHandlePropertyType (compiler, prop, targetObject, propDesc) || tryHandlePropertyType (compiler, prop, targetObject, propDesc) || tryHandlePropertyType (compiler, prop, targetObject, propDesc) || tryHandlePropertyType (compiler, prop, targetObject, propDesc) || tryHandlePropertyType (compiler, prop, targetObject, propDesc) || tryHandlePropertyType (compiler, prop, targetObject, propDesc) || tryHandlePropertyType (compiler, prop, targetObject, propDesc) || tryHandlePropertyType (compiler, prop, targetObject, propDesc) || tryHandlePropertyType (compiler, prop, targetObject, propDesc); if (!handled) { compiler->addError (ScriptCompiler::CE_UNEXPECTEDTOKEN, prop->file, prop->line, "property \"" + prop->name + "\" is found in type descriptor but has " "unsupported type. Mangled typeid is '" + propDesc->getValueTypeId().name() + "'"); } } void TypeDescriptorScriptTranslator::translate (ScriptCompiler* compiler, const AbstractNodePtr& node) { //LogManager::getSingleton ().logMessage ("TypeDescriptorScriptTranslator::translate begin"); // Check type descriptor was set. assert (getTypeDescriptor () && "Type descriptor must be set before we can translate."); // Fetch target object. ObjectAbstractNode *objNode = reinterpret_cast(node.get()); assert (!objNode->context.isEmpty ()); void* targetObject = any_cast (objNode->context); assert (targetObject); for (AbstractNodeList::iterator i = objNode->children.begin(); i != objNode->children.end(); ++i) { if ((*i)->type == ANT_PROPERTY) { PropertyAbstractNode *prop = reinterpret_cast((*i).get()); translateProperty (compiler, prop, targetObject, getTypeDescriptor()); } else if((*i)->type == ANT_OBJECT) { compiler->addError (ScriptCompiler::CE_INVALIDPARAMETERS, (*i)->file, (*i)->line); } } //LogManager::getSingleton ().logMessage ("TypeDescriptorScriptTranslator::translate end"); } CaelumSystemScriptTranslator::CaelumSystemScriptTranslator (): mResourceManager(), mTranslationTarget(0), mTranslationTargetFound(false), mTypeDescriptor(0) { } void CaelumSystemScriptTranslator::setTranslationTarget (CaelumSystem* target, const Ogre::String& name) { assert (target != 0); this->mTranslationTarget = target; this->mTranslationTargetName = name; this->mTranslationTargetFound = false; } void CaelumSystemScriptTranslator::clearTranslationTarget () { this->mTranslationTarget = 0; this->mTranslationTargetName.clear(); this->mTranslationTargetFound = false; } void CaelumSystemScriptTranslator::translate (ScriptCompiler* compiler, const AbstractNodePtr& node) { //LogManager::getSingleton ().logMessage ("CaelumSystemScriptTranslator::translate begin"); ObjectAbstractNode *objNode = reinterpret_cast(node.get()); CaelumSystem* sys = 0; // Check for a translation target. if (this->getTranslationTarget ()) { sys = this->getTranslationTarget (); // Check for a name match. if (this->getTranslationTargetName () != objNode->name) { //LogManager::getSingleton ().logMessage ( // "Caelum: Skipped " + objNode->cls + " name " + objNode->name + " while loading"); return; } // Clear the target; this ensure that properties which are not // mentioned are set to their default values. // We only do this after we found a target; this ensure that if // the target is not found it's not modified either. sys->clear(); //LogManager::getSingleton ().logMessage ( // "Caelum: Found " + objNode->cls + " name " + objNode->name + "; filling properties."); mTranslationTargetFound = true; } else if (this->getResourceManager ()) { // If we don't have a target but have a resource manager then create a resource. //LogManager::getSingleton ().logMessage ( // "Caelum: Saved " + objNode->cls + " name " + objNode->name + " as a resource"); PropScriptResourceManager* mgr = this->getResourceManager (); #if OGRE_VERSION >= 0x00010900 ResourcePtr resource = mgr->createResource (objNode->name, compiler->getResourceGroup()); #else ResourcePtr resource = mgr->create (objNode->name, compiler->getResourceGroup()); #endif resource->_notifyOrigin (objNode->file); return; } objNode->context = sys; for (AbstractNodeList::iterator i = objNode->children.begin(); i != objNode->children.end(); ++i) { if ((*i)->type == ANT_PROPERTY) { PropertyAbstractNode *prop = reinterpret_cast((*i).get()); // Properties implemented through type descriptor. TypeDescriptorScriptTranslator::translateProperty( compiler, prop, static_cast(sys), getTypeDescriptor ()); } else if((*i)->type == ANT_OBJECT) { ObjectAbstractNode *childObjNode = reinterpret_cast((*i).get()); //LogManager::getSingleton ().logMessage ("CaelumSystemScriptTranslator::translate child object" // " value '" + childObjNode->getValue () + "'" // " name '" + childObjNode->name + "'" // " cls '" + childObjNode->cls + "'" // " base '" + childObjNode->base + "'"); // Only allow declarations with one class token; like "moon { }" #if OGRE_VERSION < 0x010700 if (childObjNode->name.empty () == false || childObjNode->base.empty () == false) { #else if (childObjNode->name.empty () == false || childObjNode->bases.size () != 0) { #endif compiler->addError ( ScriptCompiler::CE_FEWERPARAMETERSEXPECTED, childObjNode->file, childObjNode->line, "caelum_sky_system components can't have names or bases"); continue; } const String& className = childObjNode->cls; try { if (className == "sun") { sys->setSun (new Sun (sys->getSceneMgr (), sys->getCaelumCameraNode ())); childObjNode->context = static_cast (sys->getSun ()); } else if (className == "sky_dome") { sys->setSkyDome (new SkyDome (sys->getSceneMgr (), sys->getCaelumCameraNode ())); childObjNode->context = static_cast(sys->getSkyDome ()); } else if (className == "moon") { sys->setMoon (new Moon (sys->getSceneMgr (), sys->getCaelumCameraNode ())); childObjNode->context = static_cast(sys->getMoon ()); } else if (className == "ground_fog") { sys->setGroundFog (new GroundFog (sys->getSceneMgr (), sys->getCaelumCameraNode ())); childObjNode->context = static_cast(sys->getGroundFog ()); } else if (className == "depth_composer") { sys->setDepthComposer (new DepthComposer (sys->getSceneMgr ())); childObjNode->context = static_cast(sys->getDepthComposer ()); } else if (className == "point_starfield") { sys->setPointStarfield (new PointStarfield (sys->getSceneMgr (), sys->getCaelumCameraNode())); childObjNode->context = static_cast(sys->getPointStarfield ()); } else if (className == "precipitation") { sys->setPrecipitationController (new PrecipitationController (sys->getSceneMgr ())); childObjNode->context = static_cast(sys->getPrecipitationController ()); } else if (className == "cloud_system") { sys->setCloudSystem (new CloudSystem (sys->getSceneMgr (), sys->getCaelumGroundNode ())); childObjNode->context = static_cast(sys->getCloudSystem ()); } else { LogManager::getSingleton ().logMessage ("CaelumSystemScriptTranslator::translate " "unknown child object class '" + className + "'"); } } catch (Caelum::UnsupportedException& ex) { // Catch all unsupported exceptions and report them. // This should usually happen because the proper shaders are not supported by hardware. // // Script parsing should still succeed. compiler->addError ( ScriptCompiler::CE_UNSUPPORTEDBYRENDERSYSTEM, childObjNode->file, childObjNode->line, "Failed to create component \"" + className + "\": " + ex.getFullDescription ()); continue; } processNode (compiler, *i); } } //LogManager::getSingleton ().logMessage ("SkySystemScriptTranslator::translate END"); } void CloudSystemScriptTranslator::translate (ScriptCompiler* compiler, const AbstractNodePtr& node) { //LogManager::getSingleton ().logMessage ("SkySystemScriptTranslator::translate begin"); ObjectAbstractNode *objNode = reinterpret_cast(node.get()); assert (!objNode->context.isEmpty ()); void* rawTargetObject = any_cast (objNode->context); assert (rawTargetObject); CloudSystem* target = static_cast(rawTargetObject); for (AbstractNodeList::iterator i = objNode->children.begin(); i != objNode->children.end(); ++i) { if ((*i)->type == ANT_PROPERTY) { compiler->addError ( ScriptCompiler::CE_INVALIDPARAMETERS, objNode->file, objNode->line, "cloud_system doesn't have any properties"); } else if((*i)->type == ANT_OBJECT) { ObjectAbstractNode *childObjNode = reinterpret_cast((*i).get()); /* LogManager::getSingleton ().logMessage ("CloudSystemScriptTranslator::translate child object" " value '" + childObjNode->getValue () + "'" " name '" + childObjNode->name + "'" " cls '" + childObjNode->cls + "'" " base '" + childObjNode->base + "'"); */ const Ogre::String& className = childObjNode->cls; if (className == "cloud_layer") { // Don't allow names. #if OGRE_VERSION < 0x010700 if (childObjNode->base.empty () == false) { #else if (childObjNode->bases.size () != 0) { #endif compiler->addError ( ScriptCompiler::CE_FEWERPARAMETERSEXPECTED, childObjNode->file, childObjNode->line, "cloud_layer can't have a base"); continue; } // Height here is irrelevant. It's silly to have it as a FlatCloudLayer ctor parameter. target->createLayerAtHeight (0); FlatCloudLayer* layer = target->getLayer (target->getLayerCount () - 1); // Add the new layer as a context for the object node. // This will eventually pass to the TypeDescriptorScriptTranslator for a cloud layer. childObjNode->context = static_cast(layer); } else { LogManager::getSingleton ().logMessage ("CloudSystemScriptTranslator::translate " "unknown child object class '" + className + "'"); } processNode (compiler, *i); } } //LogManager::getSingleton ().logMessage ("CloudSystemScriptTranslator::translate END"); } CaelumScriptTranslatorManager::CaelumScriptTranslatorManager ( CaelumDefaultTypeDescriptorData* typeData ): mCaelumSystemTranslator(), mCloudSystemTranslator(), mFlatCloudLayerTranslator(typeData->FlatCloudLayerTypeDescriptor), mSunTranslator(typeData->BaseSkyLightTypeDescriptor), mMoonTranslator(typeData->BaseSkyLightTypeDescriptor), mPointStarfieldTranslator(typeData->PointStarfieldTypeDescriptor), mGroundFogTranslator(typeData->GroundFogTypeDescriptor), mDepthComposerTranslator(typeData->DepthComposerTypeDescriptor), mPrecipitationTranslator(typeData->PrecipitationTypeDescriptor), mSkyDomeTranslator(typeData->SkyDomeTypeDescriptor) { mCaelumSystemTranslator.setTypeDescriptor(typeData->CaelumSystemTypeDescriptor); // Build translator map to member translators. mTranslatorMap.insert (std::make_pair ("caelum_sky_system", &mCaelumSystemTranslator)); mTranslatorMap.insert (std::make_pair ("cloud_system", &mCloudSystemTranslator)); mTranslatorMap.insert (std::make_pair ("cloud_layer", &mFlatCloudLayerTranslator)); mTranslatorMap.insert (std::make_pair ("sun", &mSunTranslator)); mTranslatorMap.insert (std::make_pair ("moon", &mMoonTranslator)); mTranslatorMap.insert (std::make_pair ("point_starfield", &mPointStarfieldTranslator)); mTranslatorMap.insert (std::make_pair ("ground_fog", &mGroundFogTranslator)); mTranslatorMap.insert (std::make_pair ("depth_composer", &mDepthComposerTranslator)); mTranslatorMap.insert (std::make_pair ("precipitation", &mPrecipitationTranslator)); mTranslatorMap.insert (std::make_pair ("sky_dome", &mSkyDomeTranslator)); } size_t CaelumScriptTranslatorManager::getNumTranslators () const { // Turns out this is never called. assert(0 && "This method should be removed from Ogre::ScriptTranslatorManager"); return mTranslatorMap.size (); } void CaelumScriptTranslatorManager::_setPropScriptResourceManager (PropScriptResourceManager* mgr) { mCaelumSystemTranslator.setResourceManager (mgr); } ScriptTranslator* CaelumScriptTranslatorManager::getTranslator (const AbstractNodePtr& node) { //LogManager::getSingleton ().logMessage ("CaelumScriptTranslatorManager::getTranslator"); if (node->type == ANT_ATOM) { //ObjectAbstractNode* atomNode = reinterpret_cast(node.get()); //LogManager::getSingleton ().logMessage ("CaelumScriptTranslatorManager::getTranslator atom node " + atomNode->getValue ()); } else if (node->type == ANT_OBJECT) { ObjectAbstractNode* objNode = reinterpret_cast(node.get()); //LogManager::getSingleton ().logMessage ("CaelumScriptTranslatorManager::getTranslator object node " + objNode->getValue ()); // Pass down the context. ScriptTranslatorMap::const_iterator it = mTranslatorMap.find(objNode->cls); if (it != mTranslatorMap.end()) { return it->second; } } // Not found in this manager. return 0; } } #endif // CAELUM_SCRIPT_SUPPORT