Browse Source

Removed some exceptions. Now failing to load a resource returns a null pointer instead of throwing an exception.

Lasse Öörni 15 years ago
parent
commit
fba7a7dba1

+ 1 - 1
Engine/Common/Variant.cpp

@@ -189,7 +189,7 @@ void Variant::read(Deserializer& source)
 void Variant::setBuffer(const void* data, unsigned size)
 void Variant::setBuffer(const void* data, unsigned size)
 {
 {
     if ((size) && (!data))
     if ((size) && (!data))
-        EXCEPTION("Null Variant buffer source");
+        SAFE_EXCEPTION("Null Variant buffer source");
     
     
     mType = VAR_BUFFER;
     mType = VAR_BUFFER;
     mValue.mBuffer.resize(size);
     mValue.mBuffer.resize(size);

+ 3 - 9
Engine/Engine/Console.cpp

@@ -171,15 +171,9 @@ void Console::updateElements()
     
     
     if (mFont)
     if (mFont)
     {
     {
-        try
-        {
-            for (unsigned i = 0; i < mRows.size(); ++i)
-                mRows[i]->setFont(mFont, mFontSize);
-            mLineEdit->getTextElement()->setFont(mFont, mFontSize);
-        }
-        catch (...)
-        {
-        }
+        for (unsigned i = 0; i < mRows.size(); ++i)
+            mRows[i]->setFont(mFont, mFontSize);
+        mLineEdit->getTextElement()->setFont(mFont, mFontSize);
     }
     }
     
     
     mLineEdit->setWidth(width - 8);
     mLineEdit->setWidth(width - 8);

+ 2 - 1
Engine/Engine/ParticleEmitter.cpp

@@ -22,6 +22,7 @@
 //
 //
 
 
 #include "Precompiled.h"
 #include "Precompiled.h"
+#include "Log.h"
 #include "Material.h"
 #include "Material.h"
 #include "ParticleEmitter.h"
 #include "ParticleEmitter.h"
 #include "Profiler.h"
 #include "Profiler.h"
@@ -382,7 +383,7 @@ void ParticleEmitter::loadParameters(XMLFile* file, ResourceCache* cache)
         else if (type == "sphere")
         else if (type == "sphere")
             mEmitterType = EMITTER_SPHERE;
             mEmitterType = EMITTER_SPHERE;
         else
         else
-            EXCEPTION("Unknown particle emitter type " + type);
+            LOGERROR("Unknown particle emitter type " + type);
     }
     }
     
     
     if (rootElem.hasChildElement("emittersize"))
     if (rootElem.hasChildElement("emittersize"))

+ 4 - 4
Engine/Engine/RegisterRenderer.cpp

@@ -589,7 +589,7 @@ static void registerZone(asIScriptEngine* engine)
 static void registerStaticModel(asIScriptEngine* engine)
 static void registerStaticModel(asIScriptEngine* engine)
 {
 {
     registerGeometryNode<StaticModel>(engine, "StaticModel");
     registerGeometryNode<StaticModel>(engine, "StaticModel");
-    engine->RegisterObjectMethod("StaticModel", "bool setModel(Model@+)", asMETHOD(StaticModel, setModel), asCALL_THISCALL);
+    engine->RegisterObjectMethod("StaticModel", "void setModel(Model@+)", asMETHOD(StaticModel, setModel), asCALL_THISCALL);
     engine->RegisterObjectMethod("StaticModel", "void setMaterial(Material@+)", asMETHODPR(StaticModel, setMaterial, (Material*), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("StaticModel", "void setMaterial(Material@+)", asMETHODPR(StaticModel, setMaterial, (Material*), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("StaticModel", "bool setMaterial(uint, Material@+)", asMETHODPR(StaticModel, setMaterial, (unsigned, Material*), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("StaticModel", "bool setMaterial(uint, Material@+)", asMETHODPR(StaticModel, setMaterial, (unsigned, Material*), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("StaticModel", "const BoundingBox& getBoundingBox() const", asMETHOD(StaticModel, getBoundingBox), asCALL_THISCALL);
     engine->RegisterObjectMethod("StaticModel", "const BoundingBox& getBoundingBox() const", asMETHOD(StaticModel, getBoundingBox), asCALL_THISCALL);
@@ -603,7 +603,7 @@ static void registerStaticModel(asIScriptEngine* engine)
 static void registerSkybox(asIScriptEngine* engine)
 static void registerSkybox(asIScriptEngine* engine)
 {
 {
     registerGeometryNode<Skybox>(engine, "Skybox");
     registerGeometryNode<Skybox>(engine, "Skybox");
-    engine->RegisterObjectMethod("Skybox", "bool setModel(Model@+)", asMETHOD(Skybox, setModel), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Skybox", "void setModel(Model@+)", asMETHOD(Skybox, setModel), asCALL_THISCALL);
     engine->RegisterObjectMethod("Skybox", "void setMaterial(Material@+)", asMETHODPR(Skybox, setMaterial, (Material*), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("Skybox", "void setMaterial(Material@+)", asMETHODPR(Skybox, setMaterial, (Material*), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("Skybox", "bool setMaterial(uint, Material@+)", asMETHODPR(Skybox, setMaterial, (unsigned, Material*), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("Skybox", "bool setMaterial(uint, Material@+)", asMETHODPR(Skybox, setMaterial, (unsigned, Material*), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("Skybox", "const BoundingBox& getBoundingBox() const", asMETHOD(Skybox, getBoundingBox), asCALL_THISCALL);
     engine->RegisterObjectMethod("Skybox", "const BoundingBox& getBoundingBox() const", asMETHOD(Skybox, getBoundingBox), asCALL_THISCALL);
@@ -662,7 +662,7 @@ static void registerAnimatedModel(asIScriptEngine* engine)
     engine->RegisterObjectMethod("AnimationState", "bool getUseNlerp() const", asMETHOD(AnimationState, getUseNlerp), asCALL_THISCALL);
     engine->RegisterObjectMethod("AnimationState", "bool getUseNlerp() const", asMETHOD(AnimationState, getUseNlerp), asCALL_THISCALL);
     
     
     registerGeometryNode<AnimatedModel>(engine, "AnimatedModel");
     registerGeometryNode<AnimatedModel>(engine, "AnimatedModel");
-    engine->RegisterObjectMethod("AnimatedModel", "bool setModel(Model@+)", asMETHOD(AnimatedModel, setModel), asCALL_THISCALL);
+    engine->RegisterObjectMethod("AnimatedModel", "void setModel(Model@+)", asMETHOD(AnimatedModel, setModel), asCALL_THISCALL);
     engine->RegisterObjectMethod("AnimatedModel", "void setMaterial(Material@+)", asMETHODPR(AnimatedModel, setMaterial, (Material*), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("AnimatedModel", "void setMaterial(Material@+)", asMETHODPR(AnimatedModel, setMaterial, (Material*), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("AnimatedModel", "bool setMaterial(uint, Material@+)", asMETHODPR(AnimatedModel, setMaterial, (unsigned, Material*), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("AnimatedModel", "bool setMaterial(uint, Material@+)", asMETHODPR(AnimatedModel, setMaterial, (unsigned, Material*), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("AnimatedModel", "const BoundingBox& getBoundingBox() const", asMETHOD(AnimatedModel, getBoundingBox), asCALL_THISCALL);
     engine->RegisterObjectMethod("AnimatedModel", "const BoundingBox& getBoundingBox() const", asMETHOD(AnimatedModel, getBoundingBox), asCALL_THISCALL);
@@ -735,7 +735,7 @@ static void registerInstancedModel(asIScriptEngine* engine)
     engine->RegisterObjectProperty("Instance", "Vector3 scale", offsetof(Instance, mScale));
     engine->RegisterObjectProperty("Instance", "Vector3 scale", offsetof(Instance, mScale));
     
     
     registerGeometryNode<InstancedModel>(engine, "InstancedModel");
     registerGeometryNode<InstancedModel>(engine, "InstancedModel");
-    engine->RegisterObjectMethod("InstancedModel", "bool setModel(Model@+)", asMETHOD(InstancedModel, setModel), asCALL_THISCALL);
+    engine->RegisterObjectMethod("InstancedModel", "void setModel(Model@+)", asMETHOD(InstancedModel, setModel), asCALL_THISCALL);
     engine->RegisterObjectMethod("InstancedModel", "void setMaterial(Material@+)", asMETHODPR(InstancedModel, setMaterial, (Material*), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("InstancedModel", "void setMaterial(Material@+)", asMETHODPR(InstancedModel, setMaterial, (Material*), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("InstancedModel", "bool setMaterial(uint, Material@+)", asMETHODPR(InstancedModel, setMaterial, (unsigned, Material*), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("InstancedModel", "bool setMaterial(uint, Material@+)", asMETHODPR(InstancedModel, setMaterial, (unsigned, Material*), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("InstancedModel", "void setNumInstances(uint)", asMETHOD(InstancedModel, setNumInstances), asCALL_THISCALL);
     engine->RegisterObjectMethod("InstancedModel", "void setNumInstances(uint)", asMETHOD(InstancedModel, setNumInstances), asCALL_THISCALL);

+ 2 - 21
Engine/Engine/RegisterResource.cpp

@@ -35,14 +35,7 @@ void registerResource(asIScriptEngine* engine)
 
 
 static Resource* ResourceCacheGetResource(const std::string& type, const std::string& name, ResourceCache* ptr)
 static Resource* ResourceCacheGetResource(const std::string& type, const std::string& name, ResourceCache* ptr)
 {
 {
-    try
-    {
-        return ptr->getResource(ShortStringHash(type), name);
-    }
-    catch (Exception& e)
-    {
-        SAFE_RETHROW_RET(e, 0);
-    }
+    return ptr->getResource(ShortStringHash(type), name);
 }
 }
 
 
 static File* ResourceCacheGetFile(const std::string& name, ResourceCache* ptr)
 static File* ResourceCacheGetFile(const std::string& name, ResourceCache* ptr)
@@ -77,18 +70,6 @@ static void ResourceCacheReleaseResourcesPartial(const std::string& type, const
     ptr->releaseResources(ShortStringHash(type), partialName, force);
     ptr->releaseResources(ShortStringHash(type), partialName, force);
 }
 }
 
 
-static void ResourceCacheReloadResource(Resource* resource, ResourceCache* ptr)
-{
-    try
-    {
-        return ptr->reloadResource(resource);
-    }
-    catch (Exception& e)
-    {
-        SAFE_RETHROW(e);
-    }
-}
-
 static void ResourceCacheSetMemoryBudget(const std::string& type, unsigned budget, ResourceCache* ptr)
 static void ResourceCacheSetMemoryBudget(const std::string& type, unsigned budget, ResourceCache* ptr)
 {
 {
     ptr->setMemoryBudget(ShortStringHash(type), budget);
     ptr->setMemoryBudget(ShortStringHash(type), budget);
@@ -122,7 +103,7 @@ static void registerResourceCache(asIScriptEngine* engine)
     engine->RegisterObjectMethod("ResourceCache", "void releaseResource(const string& in, const string& in, bool)", asFUNCTION(ResourceCacheReleaseResource), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("ResourceCache", "void releaseResource(const string& in, const string& in, bool)", asFUNCTION(ResourceCacheReleaseResource), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("ResourceCache", "void releaseResources(const string& in, bool)", asFUNCTION(ResourceCacheReleaseResources), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("ResourceCache", "void releaseResources(const string& in, bool)", asFUNCTION(ResourceCacheReleaseResources), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("ResourceCache", "void releaseResources(const string& in, const string& in, bool)", asFUNCTION(ResourceCacheReleaseResourcesPartial), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("ResourceCache", "void releaseResources(const string& in, const string& in, bool)", asFUNCTION(ResourceCacheReleaseResourcesPartial), asCALL_CDECL_OBJLAST);
-    engine->RegisterObjectMethod("ResourceCache", "void reloadResource(Resource@+)", asFUNCTION(ResourceCacheReloadResource), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod("ResourceCache", "bool reloadResource(Resource@+)", asMETHOD(ResourceCache, reloadResource), asCALL_THISCALL);
     engine->RegisterObjectMethod("ResourceCache", "void setMemoryBudget(const string& in, uint)", asFUNCTION(ResourceCacheSetMemoryBudget), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("ResourceCache", "void setMemoryBudget(const string& in, uint)", asFUNCTION(ResourceCacheSetMemoryBudget), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("ResourceCache", "bool exists(const string& in) const", asMETHODPR(ResourceCache, exists, (const std::string&) const, bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("ResourceCache", "bool exists(const string& in) const", asMETHODPR(ResourceCache, exists, (const std::string&) const, bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("ResourceCache", "File@ getFile(const string& in)", asFUNCTION(ResourceCacheGetFile), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("ResourceCache", "File@ getFile(const string& in)", asFUNCTION(ResourceCacheGetFile), asCALL_CDECL_OBJLAST);

+ 13 - 7
Engine/Physics/CollisionShape.cpp

@@ -373,15 +373,12 @@ void CollisionShape::load(Deserializer& source, ResourceCache* cache)
         
         
         if (type == "box")
         if (type == "box")
         {
         {
-            Vector3 size;
+            Vector3 size = Vector3::sZero;
             
             
             if (shapeElem.hasAttribute("size"))
             if (shapeElem.hasAttribute("size"))
                 size = shapeElem.getVector3("size");
                 size = shapeElem.getVector3("size");
             else if (shapeElem.hasAttribute("halfsize"))
             else if (shapeElem.hasAttribute("halfsize"))
                 size = shapeElem.getVector3("halfsize") * 2.0f;
                 size = shapeElem.getVector3("halfsize") * 2.0f;
-            else
-                EXCEPTION("Box without size defined");
-            
             addBox(size, position, rotation);
             addBox(size, position, rotation);
         }
         }
         
         
@@ -491,7 +488,10 @@ void CollisionShape::addTriangleMesh(const Model* model, unsigned lodLevel, cons
     PROFILE(Physics_AddTriangleMesh);
     PROFILE(Physics_AddTriangleMesh);
     
     
     if (!model)
     if (!model)
-        EXCEPTION("Null model for addTriangleMesh");
+    {
+        LOGERROR("Null model for addTriangleMesh");
+        return;
+    }
     
     
     CollisionSubShape newShape;
     CollisionSubShape newShape;
     newShape.mType = SHAPE_TRIANGLEMESH;
     newShape.mType = SHAPE_TRIANGLEMESH;
@@ -520,7 +520,10 @@ void CollisionShape::addHeightfield(const Model* model, unsigned xPoints, unsign
     PROFILE(Physics_AddTriangleMesh);
     PROFILE(Physics_AddTriangleMesh);
     
     
     if (!model)
     if (!model)
-        EXCEPTION("Null model for addHeightField");
+    {
+        LOGERROR("Null model for addHeightField");
+        return;
+    }
     
     
     CollisionSubShape newShape;
     CollisionSubShape newShape;
     newShape.mType = SHAPE_HEIGHTFIELD;
     newShape.mType = SHAPE_HEIGHTFIELD;
@@ -549,7 +552,10 @@ void CollisionShape::addConvexHull(const Model* model, float skinWidth, unsigned
     PROFILE(Physics_AddConvexHull);
     PROFILE(Physics_AddConvexHull);
     
     
     if (!model)
     if (!model)
-        EXCEPTION("Null model for addConvexHull");
+    {
+        LOGERROR("Null model for addConvexHull");
+        return;
+    }
     
     
     CollisionSubShape newShape;
     CollisionSubShape newShape;
     newShape.mType = SHAPE_TRIANGLEMESH;
     newShape.mType = SHAPE_TRIANGLEMESH;

+ 3 - 11
Engine/Renderer/AnimatedModel.cpp

@@ -607,18 +607,15 @@ void AnimatedModel::drawDebugGeometry(DebugRenderer* debug)
     debug->addSkeleton(mSkeleton, Color(0.75f, 0.75f, 0.75f), false);
     debug->addSkeleton(mSkeleton, Color(0.75f, 0.75f, 0.75f), false);
 }
 }
 
 
-bool AnimatedModel::setModel(Model* model)
+void AnimatedModel::setModel(Model* model)
 {
 {
     if (model == mModel)
     if (model == mModel)
-        return true;
+        return;
     
     
     PROFILE(AnimatedModel_SetModel);
     PROFILE(AnimatedModel_SetModel);
     
     
     if (!model)
     if (!model)
-    {
-        LOGERROR("Null model for AnimatedModel");
-        return false;
-    }
+        return;
     
     
     mModel = model;
     mModel = model;
     
     
@@ -658,8 +655,6 @@ bool AnimatedModel::setModel(Model* model)
     // Copy bounding box & skeleton
     // Copy bounding box & skeleton
     setBoundingBox(model->getBoundingBox());
     setBoundingBox(model->getBoundingBox());
     setSkeleton(model->getSkeleton());
     setSkeleton(model->getSkeleton());
-    
-    return true;
 }
 }
 
 
 AnimationState* AnimatedModel::addAnimationState(Animation* animation)
 AnimationState* AnimatedModel::addAnimationState(Animation* animation)
@@ -668,10 +663,7 @@ AnimationState* AnimatedModel::addAnimationState(Animation* animation)
         return 0;
         return 0;
     
     
     if (!mSkeleton.getNumBones())
     if (!mSkeleton.getNumBones())
-    {
-        LOGERROR("Animations can not be added to a model without skeleton");
         return 0;
         return 0;
-    }
     
     
     // Check for not adding twice
     // Check for not adding twice
     AnimationState* existing = getAnimationState(animation);
     AnimationState* existing = getAnimationState(animation);

+ 1 - 1
Engine/Renderer/AnimatedModel.h

@@ -77,7 +77,7 @@ public:
     virtual void drawDebugGeometry(DebugRenderer* debug);
     virtual void drawDebugGeometry(DebugRenderer* debug);
     
     
     //! Set model
     //! Set model
-    bool setModel(Model* model);
+    void setModel(Model* model);
     //! Add an animation
     //! Add an animation
     AnimationState* addAnimationState(Animation* animation);
     AnimationState* addAnimationState(Animation* animation);
     //! Remove an animation by animation pointer
     //! Remove an animation by animation pointer

+ 12 - 15
Engine/Renderer/AnimationState.cpp

@@ -41,7 +41,18 @@ AnimationState::AnimationState(AnimatedModel* node, Animation* animation) :
     mUseNlerp(false),
     mUseNlerp(false),
     mInterpolationFlags(0)
     mInterpolationFlags(0)
 {
 {
-    setAnimation(animation);
+    if (!node)
+        EXCEPTION("Null node for AnimationState");
+    if (!animation)
+        EXCEPTION("Null animation for AnimationState");
+    
+    mAnimation = animation;
+    setStartBone(0);
+    
+    // Setup a cache for last keyframe of each track
+    mLastKeyFrame.resize(mAnimation->getNumTracks());
+    for (unsigned i = 0; i < mLastKeyFrame.size(); ++i)
+        mLastKeyFrame[i] = 0;
 }
 }
 
 
 AnimationState::~AnimationState()
 AnimationState::~AnimationState()
@@ -109,20 +120,6 @@ void AnimationState::loadXML(const XMLElement& element)
     setUseNlerp(element.getBool("nlerp"));
     setUseNlerp(element.getBool("nlerp"));
 }
 }
 
 
-void AnimationState::setAnimation(Animation* animation)
-{
-    if (!animation)
-        EXCEPTION("Null animation for AnimationState");
-    
-    mAnimation = animation;
-    setStartBone(0);
-    
-    // Setup a cache for last keyframe of each track
-    mLastKeyFrame.resize(mAnimation->getNumTracks());
-    for (unsigned i = 0; i < mLastKeyFrame.size(); ++i)
-        mLastKeyFrame[i] = 0;
-}
-
 void AnimationState::setStartBone(Bone* startBone)
 void AnimationState::setStartBone(Bone* startBone)
 {
 {
     Bone* rootBone = mNode->getSkeleton().getRootBone();
     Bone* rootBone = mNode->getSkeleton().getRootBone();

+ 0 - 2
Engine/Renderer/AnimationState.h

@@ -54,8 +54,6 @@ public:
     void saveXML(XMLElement& element);
     void saveXML(XMLElement& element);
     //! Read state from an XML element
     //! Read state from an XML element
     void loadXML(const XMLElement& element);
     void loadXML(const XMLElement& element);
-    //! Set animation
-    void setAnimation(Animation* animation);
     //! Set start bone
     //! Set start bone
     void setStartBone(Bone* startBone);
     void setStartBone(Bone* startBone);
     //! Set looping enabled/disabled
     //! Set looping enabled/disabled

+ 5 - 10
Engine/Renderer/InstancedModel.cpp

@@ -534,18 +534,15 @@ bool InstancedModel::drawOcclusion(OcclusionBuffer* buffer)
     return success;
     return success;
 }
 }
 
 
-bool InstancedModel::setModel(Model* model)
+void InstancedModel::setModel(Model* model)
 {
 {
+    if (model == mModel)
+        return;
+    
     PROFILE(InstancedModel_SetModel);
     PROFILE(InstancedModel_SetModel);
     
     
     if (!model)
     if (!model)
-    {
-        LOGERROR("Null model for InstancedModel");
-        return false;
-    }
-    
-    if (model == mModel)
-        return true;
+        return;
     
     
     mModel = model;
     mModel = model;
     mOriginalGeometries.clear();
     mOriginalGeometries.clear();
@@ -563,8 +560,6 @@ bool InstancedModel::setModel(Model* model)
     setBoundingBox(model->getBoundingBox());
     setBoundingBox(model->getBoundingBox());
     markInstancesDirty();
     markInstancesDirty();
     resetLodLevels();
     resetLodLevels();
-    
-    return true;
 }
 }
 
 
 void InstancedModel::setMaterial(Material* material)
 void InstancedModel::setMaterial(Material* material)

+ 1 - 1
Engine/Renderer/InstancedModel.h

@@ -88,7 +88,7 @@ public:
     virtual bool drawOcclusion(OcclusionBuffer* buffer);
     virtual bool drawOcclusion(OcclusionBuffer* buffer);
     
     
     //! Set model
     //! Set model
-    bool setModel(Model* model);
+    void setModel(Model* model);
     //! Set material on all geometries
     //! Set material on all geometries
     void setMaterial(Material* material);
     void setMaterial(Material* material);
     //! Set material on one geometry
     //! Set material on one geometry

+ 57 - 51
Engine/Renderer/Material.cpp

@@ -380,7 +380,7 @@ void Material::load(Deserializer& source, ResourceCache* cache)
             TextureUnit unit = TU_DIFFUSE;
             TextureUnit unit = TU_DIFFUSE;
             if (textureElem.hasAttribute("unit"))
             if (textureElem.hasAttribute("unit"))
             {
             {
-                const std::string unitName = textureElem.getStringLower("unit");
+                std::string unitName = textureElem.getStringLower("unit");
                 unit = (TextureUnit)getIndexFromStringList(unitName, textureUnitNames, MAX_MATERIAL_TEXTURE_UNITS,
                 unit = (TextureUnit)getIndexFromStringList(unitName, textureUnitNames, MAX_MATERIAL_TEXTURE_UNITS,
                     MAX_MATERIAL_TEXTURE_UNITS);
                     MAX_MATERIAL_TEXTURE_UNITS);
                 if (unitName == "diff")
                 if (unitName == "diff")
@@ -392,14 +392,17 @@ void Material::load(Deserializer& source, ResourceCache* cache)
                 if (unitName == "env")
                 if (unitName == "env")
                     unit = TU_ENVIRONMENT;
                     unit = TU_ENVIRONMENT;
                 if (unit == MAX_MATERIAL_TEXTURE_UNITS)
                 if (unit == MAX_MATERIAL_TEXTURE_UNITS)
-                    EXCEPTION("Unknown texture unit " + unitName);
+                    LOGERROR("Unknown texture unit " + unitName);
+            }
+            if (unit != MAX_MATERIAL_TEXTURE_UNITS)
+            {
+                std::string name = textureElem.getString("name");
+                // Detect cube maps by file extension: they are defined by an XML file
+                if (getExtension(name) == ".xml")
+                    newTechnique.setTexture(unit, cache->getResource<TextureCube>(name));
+                else
+                    newTechnique.setTexture(unit, cache->getResource<Texture2D>(name));
             }
             }
-            std::string name = textureElem.getString("name");
-            // Detect cube maps by file extension: they are defined by an XML file
-            if (getExtension(name) == ".xml")
-                newTechnique.setTexture(unit, cache->getResource<TextureCube>(name));
-            else
-                newTechnique.setTexture(unit, cache->getResource<Texture2D>(name));
             textureElem = textureElem.getNextElement("texture");
             textureElem = textureElem.getNextElement("texture");
         }
         }
         
         
@@ -417,7 +420,7 @@ void Material::load(Deserializer& source, ResourceCache* cache)
                 if (psParam != MAX_PS_PARAMETERS)
                 if (psParam != MAX_PS_PARAMETERS)
                     newTechnique.setPixelShaderParameter(psParam, value);
                     newTechnique.setPixelShaderParameter(psParam, value);
                 else
                 else
-                    EXCEPTION("Unknown shader parameter " + name);
+                    LOGERROR("Unknown shader parameter " + name);
             }
             }
             
             
             parameterElem = parameterElem.getNextElement("parameter");
             parameterElem = parameterElem.getNextElement("parameter");
@@ -436,54 +439,57 @@ void Material::load(Deserializer& source, ResourceCache* cache)
                 if (name == "custom")
                 if (name == "custom")
                     type = PASS_POSTOPAQUE;
                     type = PASS_POSTOPAQUE;
                 if (type == MAX_PASSES)
                 if (type == MAX_PASSES)
-                    EXCEPTION("Unknown pass " + name);
+                    LOGERROR("Unknown pass " + name);
             }
             }
             else
             else
-                EXCEPTION("Missing pass name");
-            
-            MaterialPass& newPass = *newTechnique.createPass(type);
-            
-            if (passElem.hasAttribute("vs"))
-                newPass.setVertexShader(passElem.getString("vs"));
-            
-            if (passElem.hasAttribute("ps"))
-                newPass.setPixelShader(passElem.getString("ps"));
-            
-            if (passElem.hasAttribute("alphamask"))
-                newPass.setAlphaMask(passElem.getBool("alphamask"));
-            
-            if (passElem.hasAttribute("alphatest"))
-                newPass.setAlphaTest(passElem.getBool("alphatest"));
+                LOGERROR("Missing pass name");
             
             
-            if (passElem.hasAttribute("blend"))
+            if (type != MAX_PASSES)
             {
             {
-                std::string blend = passElem.getStringLower("blend");
-                newPass.setBlendMode((BlendMode)getIndexFromStringList(blend, blendModeNames, MAX_BLENDMODES, BLEND_REPLACE));
+                MaterialPass& newPass = *newTechnique.createPass(type);
+                
+                if (passElem.hasAttribute("vs"))
+                    newPass.setVertexShader(passElem.getString("vs"));
+                
+                if (passElem.hasAttribute("ps"))
+                    newPass.setPixelShader(passElem.getString("ps"));
+                
+                if (passElem.hasAttribute("alphamask"))
+                    newPass.setAlphaMask(passElem.getBool("alphamask"));
+                
+                if (passElem.hasAttribute("alphatest"))
+                    newPass.setAlphaTest(passElem.getBool("alphatest"));
+                
+                if (passElem.hasAttribute("blend"))
+                {
+                    std::string blend = passElem.getStringLower("blend");
+                    newPass.setBlendMode((BlendMode)getIndexFromStringList(blend, blendModeNames, MAX_BLENDMODES, BLEND_REPLACE));
+                }
+                
+                if (passElem.hasAttribute("cull"))
+                {
+                    std::string cull = passElem.getStringLower("cull");
+                    newPass.setCullMode((CullMode)getIndexFromStringList(cull, cullModeNames, MAX_CULLMODES, CULL_CCW));
+                }
+                
+                if (passElem.hasAttribute("depthtest"))
+                {
+                    std::string depthTest = passElem.getStringLower("depthtest");
+                    if (depthTest == "false")
+                        newPass.setDepthTestMode(CMP_ALWAYS);
+                    else
+                        newPass.setDepthTestMode((CompareMode)getIndexFromStringList(depthTest, compareModeNames, MAX_COMPAREMODES,
+                            CMP_LESSEQUAL));
+                }
+                
+                if (passElem.hasAttribute("depthwrite"))
+                    newPass.setDepthWrite(passElem.getBool("depthwrite"));
+                
+                // Undefine a pass by setting empty vertex or pixel shader name
+                if ((newPass.getVertexShaderName().empty()) || (newPass.getPixelShaderName().empty()))
+                    newTechnique.removePass(type);
             }
             }
             
             
-            if (passElem.hasAttribute("cull"))
-            {
-                std::string cull = passElem.getStringLower("cull");
-                newPass.setCullMode((CullMode)getIndexFromStringList(cull, cullModeNames, MAX_CULLMODES, CULL_CCW));
-            }
-            
-            if (passElem.hasAttribute("depthtest"))
-            {
-                std::string depthTest = passElem.getStringLower("depthtest");
-                if (depthTest == "false")
-                    newPass.setDepthTestMode(CMP_ALWAYS);
-                else
-                    newPass.setDepthTestMode((CompareMode)getIndexFromStringList(depthTest, compareModeNames, MAX_COMPAREMODES,
-                        CMP_LESSEQUAL));
-            }
-            
-            if (passElem.hasAttribute("depthwrite"))
-                newPass.setDepthWrite(passElem.getBool("depthwrite"));
-            
-            // Undefine a pass by setting empty vertex or pixel shader name
-            if ((newPass.getVertexShaderName().empty()) || (newPass.getPixelShaderName().empty()))
-                newTechnique.removePass(type);
-            
             passElem = passElem.getNextElement("pass");
             passElem = passElem.getNextElement("pass");
         }
         }
         
         

+ 5 - 10
Engine/Renderer/StaticModel.cpp

@@ -299,18 +299,15 @@ bool StaticModel::drawOcclusion(OcclusionBuffer* buffer)
     return success;
     return success;
 }
 }
 
 
-bool StaticModel::setModel(Model* model)
+void StaticModel::setModel(Model* model)
 {
 {
+    if (model == mModel)
+        return;
+      
     PROFILE(StaticModel_SetModel);
     PROFILE(StaticModel_SetModel);
     
     
     if (!model)
     if (!model)
-    {
-        LOGERROR("Null model for StaticModel");
-        return false;
-    }
-    
-    if (model == mModel)
-        return true;
+        return;
     
     
     mModel = model;
     mModel = model;
     
     
@@ -322,8 +319,6 @@ bool StaticModel::setModel(Model* model)
     
     
     setBoundingBox(model->getBoundingBox());
     setBoundingBox(model->getBoundingBox());
     resetLodLevels();
     resetLodLevels();
-    
-    return true;
 }
 }
 
 
 void StaticModel::setMaterial(Material* material)
 void StaticModel::setMaterial(Material* material)

+ 1 - 1
Engine/Renderer/StaticModel.h

@@ -66,7 +66,7 @@ public:
     virtual bool drawOcclusion(OcclusionBuffer* buffer);
     virtual bool drawOcclusion(OcclusionBuffer* buffer);
     
     
     //! Set model
     //! Set model
-    bool setModel(Model* model);
+    void setModel(Model* model);
     //! Set material on all geometries
     //! Set material on all geometries
     void setMaterial(Material* material);
     void setMaterial(Material* material);
     //! Set material on one geometry
     //! Set material on one geometry

+ 36 - 17
Engine/Resource/ResourceCache.cpp

@@ -224,15 +224,24 @@ void ResourceCache::releaseResources(ShortStringHash type, const std::string& pa
         updateResourceGroup(type);
         updateResourceGroup(type);
 }
 }
 
 
-void ResourceCache::reloadResource(Resource* resource)
+bool ResourceCache::reloadResource(Resource* resource)
 {
 {
     if (!resource)
     if (!resource)
-        return;
+        return false;
     
     
-    SharedPtr<File> file = getFile(resource->getName());
-    resource->load(*(file.getPtr()), this);
-    resource->resetUseTimer();
-    updateResourceGroup(resource->getType());
+    try
+    {
+        SharedPtr<File> file = getFile(resource->getName());
+        resource->load(*(file.getPtr()), this);
+        resource->resetUseTimer();
+        updateResourceGroup(resource->getType());
+        return true;
+    }
+    catch (...)
+    {
+        releaseResource(resource->getType(), resource->getNameHash());
+        return false;
+    }
 }
 }
 
 
 void ResourceCache::setMemoryBudget(ShortStringHash type, unsigned budget)
 void ResourceCache::setMemoryBudget(ShortStringHash type, unsigned budget)
@@ -275,7 +284,7 @@ Resource* ResourceCache::getResource(ShortStringHash type, const std::string& na
 
 
 Resource* ResourceCache::getResource(ShortStringHash type, StringHash nameHash)
 Resource* ResourceCache::getResource(ShortStringHash type, StringHash nameHash)
 {
 {
-    // Special case: null hash is allowed to return null pointer to simplify resource loading code
+    // If null hash, return null pointer immediately
     if (!nameHash)
     if (!nameHash)
         return 0;
         return 0;
     
     
@@ -294,20 +303,30 @@ Resource* ResourceCache::getResource(ShortStringHash type, StringHash nameHash)
     }
     }
     
     
     if (!resource)
     if (!resource)
-        EXCEPTION("Could not load unknown resource type " + toString(type));
+    {
+        LOGERROR("Could not load unknown resource type " + toString(type));
+        return 0;
+    }
     
     
     LOGDEBUG("Loading resource " + name);
     LOGDEBUG("Loading resource " + name);
     
     
     // Attempt to load the resource
     // Attempt to load the resource
-    SharedPtr<File> file = getFile(name);
-    resource->load(*(file.getPtr()), this);
-    resource->resetUseTimer();
-    
-    // Store to cache
-    mResourceGroups[type].mResources[nameHash] = resource;
-    updateResourceGroup(type);
-    
-    return mResourceGroups[type].mResources[nameHash];
+    try
+    {
+        SharedPtr<File> file = getFile(name);
+        resource->load(*(file.getPtr()), this);
+        resource->resetUseTimer();
+        
+        // Store to cache
+        mResourceGroups[type].mResources[nameHash] = resource;
+        updateResourceGroup(type);
+        
+        return mResourceGroups[type].mResources[nameHash];
+    }
+    catch (...)
+    {
+        return 0;
+    }
 }
 }
 
 
 std::vector<Resource*> ResourceCache::getResources(ShortStringHash type)
 std::vector<Resource*> ResourceCache::getResources(ShortStringHash type)

+ 4 - 4
Engine/Resource/ResourceCache.h

@@ -84,16 +84,16 @@ public:
     void releaseResources(ShortStringHash type, bool force = false);
     void releaseResources(ShortStringHash type, bool force = false);
     //! Release resources of a specific type and partial name
     //! Release resources of a specific type and partial name
     void releaseResources(ShortStringHash type, const std::string& partialName, bool force = false);
     void releaseResources(ShortStringHash type, const std::string& partialName, bool force = false);
-    //! Reload a resource
-    void reloadResource(Resource* resource);
+    //! Reload a resource. Return false and release it if fails
+    bool reloadResource(Resource* resource);
     //! Set memory budget for a specific resource type, default 0 is unlimited
     //! Set memory budget for a specific resource type, default 0 is unlimited
     void setMemoryBudget(ShortStringHash type, unsigned budget);
     void setMemoryBudget(ShortStringHash type, unsigned budget);
     
     
     //! Open and return a file from either the resource load paths or from inside a package file. Throw an exception if fails
     //! Open and return a file from either the resource load paths or from inside a package file. Throw an exception if fails
     SharedPtr<File> getFile(const std::string& name);
     SharedPtr<File> getFile(const std::string& name);
-    //! Return a resource by type and name. Load if not loaded yet. Throw an exception if fails
+    //! Return a resource by type and name. Load if not loaded yet. Return null if fails
     Resource* getResource(ShortStringHash type, const std::string& name);
     Resource* getResource(ShortStringHash type, const std::string& name);
-    //! Return a resource by type and name hash. Load if not loaded yet. Throw an exception if fails
+    //! Return a resource by type and name hash. Load if not loaded yet. Return null if fails
     Resource* getResource(ShortStringHash type, StringHash nameHash);
     Resource* getResource(ShortStringHash type, StringHash nameHash);
     //! Return all resources of a specific type
     //! Return all resources of a specific type
     std::vector<Resource*> getResources(ShortStringHash type);
     std::vector<Resource*> getResources(ShortStringHash type);

+ 11 - 7
Engine/Script/ScriptInstance.cpp

@@ -261,16 +261,12 @@ bool ScriptInstance::setScriptClass(ScriptFile* scriptFile, const std::string& c
     
     
     releaseObject();
     releaseObject();
     
     
-    if ((!scriptFile) || (className.empty()))
-    {
-        mScriptFile.reset();
-        mClassName = std::string();
-        return true;
-    }
-    
     mScriptFile = scriptFile;
     mScriptFile = scriptFile;
     mClassName = className;
     mClassName = className;
     
     
+    if ((!mScriptFile) && (mClassName.empty()))
+        return true;
+    
     return createObject();
     return createObject();
 }
 }
 
 
@@ -316,7 +312,15 @@ void ScriptInstance::addEventHandler(StringHash eventType, const std::string& ha
 bool ScriptInstance::createObject()
 bool ScriptInstance::createObject()
 {
 {
     if (!mScriptFile)
     if (!mScriptFile)
+    {
+        LOGERROR("Null script file for ScriptInstance");
         return false;
         return false;
+    }
+    if (mClassName.empty())
+    {
+        LOGERROR("Empty script class name");
+        return false;
+    }
     
     
     mScriptObject = mScriptFile->createObject(mClassName);
     mScriptObject = mScriptFile->createObject(mClassName);
     if (mScriptObject)
     if (mScriptObject)

+ 191 - 169
Engine/UI/Font.cpp

@@ -67,221 +67,243 @@ const FontFace* Font::getFace(int pointSize)
     if (i != mFaces.end())
     if (i != mFaces.end())
         return &i->second;
         return &i->second;
     
     
-    PROFILE(Font_GetFace);
-    
-    if (pointSize <= 0)
-        EXCEPTION("Zero or negative point size");
-    
-    if (!mFontData)
-        EXCEPTION("Font not loaded");
-    
-    FontFace newFace;
-    
-    stbtt_fontinfo fontInfo;
-    
-    // Assume 1 font per file for now
-    if (!stbtt_InitFont(&fontInfo, &mFontData[0], 0))
-        EXCEPTION("Could not initialize font");
-    
-    std::vector<bool> glyphUsed;
-    glyphUsed.resize(fontInfo.numGlyphs);
-    for (int i = 0; i < fontInfo.numGlyphs; ++i)
-        glyphUsed[i] = false;
-    
-    // Build glyph mapping and mark used glyphs
-    for (int i = 0; i < MAX_FONT_CHARS; ++i)
-    {
-        newFace.mGlyphIndex[i] = stbtt_FindGlyphIndex(&fontInfo, i);
-        glyphUsed[newFace.mGlyphIndex[i]] = true;
-    }
-    
-    // Get row height
-    int ascent, descent, lineGap;
-    stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap);
-    
-    // Calculate scale (use ascent only)
-    float scale = (float)pointSize / ascent;
-    
-    // Go through glyphs to get their dimensions & offsets
-    for (int i = 0; i < fontInfo.numGlyphs; ++i)
+    try
     {
     {
-        FontGlyph newGlyph;
+        PROFILE(Font_GetFace);
         
         
-        int ix0, iy0, ix1, iy1;
-        int advanceWidth, leftSideBearing;
+        if (pointSize <= 0)
+            EXCEPTION("Zero or negative font point size");
         
         
-        if (glyphUsed[i])
+        if (!mFontData)
+            EXCEPTION("Font not loaded");
+        
+        FontFace newFace;
+        
+        stbtt_fontinfo fontInfo;
+        
+        // Assume 1 font per file for now
+        if (!stbtt_InitFont(&fontInfo, &mFontData[0], 0))
+            EXCEPTION("Could not initialize font");
+        
+        std::vector<bool> glyphUsed;
+        glyphUsed.resize(fontInfo.numGlyphs);
+        for (int i = 0; i < fontInfo.numGlyphs; ++i)
+            glyphUsed[i] = false;
+        
+        // Build glyph mapping and mark used glyphs
+        for (int i = 0; i < MAX_FONT_CHARS; ++i)
         {
         {
-            stbtt_GetGlyphBitmapBox(&fontInfo, i, scale, scale, &ix0, &iy0, &ix1, &iy1);
-            stbtt_GetGlyphHMetrics(&fontInfo, i, &advanceWidth, &leftSideBearing);
-            newGlyph.mWidth = ix1 - ix0;
-            newGlyph.mHeight = iy1 - iy0;
-            newGlyph.mOffsetX = (int)(leftSideBearing * scale);
-            newGlyph.mOffsetY = iy0;
-            newGlyph.mAdvanceX = (int)(advanceWidth * scale);
+            newFace.mGlyphIndex[i] = stbtt_FindGlyphIndex(&fontInfo, i);
+            glyphUsed[newFace.mGlyphIndex[i]] = true;
         }
         }
-        else
+        
+        // Get row height
+        int ascent, descent, lineGap;
+        stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap);
+        
+        // Calculate scale (use ascent only)
+        float scale = (float)pointSize / ascent;
+        
+        // Go through glyphs to get their dimensions & offsets
+        for (int i = 0; i < fontInfo.numGlyphs; ++i)
         {
         {
-            newGlyph.mWidth = 0;
-            newGlyph.mHeight = 0;
-            newGlyph.mOffsetX = 0;
-            newGlyph.mOffsetY = 0;
-            newGlyph.mAdvanceX = 0;
+            FontGlyph newGlyph;
+            
+            int ix0, iy0, ix1, iy1;
+            int advanceWidth, leftSideBearing;
+            
+            if (glyphUsed[i])
+            {
+                stbtt_GetGlyphBitmapBox(&fontInfo, i, scale, scale, &ix0, &iy0, &ix1, &iy1);
+                stbtt_GetGlyphHMetrics(&fontInfo, i, &advanceWidth, &leftSideBearing);
+                newGlyph.mWidth = ix1 - ix0;
+                newGlyph.mHeight = iy1 - iy0;
+                newGlyph.mOffsetX = (int)(leftSideBearing * scale);
+                newGlyph.mOffsetY = iy0;
+                newGlyph.mAdvanceX = (int)(advanceWidth * scale);
+            }
+            else
+            {
+                newGlyph.mWidth = 0;
+                newGlyph.mHeight = 0;
+                newGlyph.mOffsetX = 0;
+                newGlyph.mOffsetY = 0;
+                newGlyph.mAdvanceX = 0;
+            }
+            
+            newFace.mGlyphs.push_back(newGlyph);
         }
         }
         
         
-        newFace.mGlyphs.push_back(newGlyph);
-    }
-    
-    // Adjust the Y-offset so that the fonts are top-aligned
-    int scaledAscent = (int)(scale * ascent);
-    for (int i = 0; i < fontInfo.numGlyphs; ++i)
-    {
-        if (glyphUsed[i])
-            newFace.mGlyphs[i].mOffsetY += scaledAscent;
-    }
-    
-    // Calculate row advance
-    newFace.mRowHeight = (int)(scale * (ascent - descent + lineGap));
-    
-    // Now try to pack into the smallest possible texture
-    int texWidth = FONT_TEXTURE_MIN_SIZE;
-    int texHeight = FONT_TEXTURE_MIN_SIZE;
-    bool doubleHorizontal = true;
-    for (;;)
-    {
-        bool success = true;
-        
-        // Check first for theoretical possible fit. If it fails, there is no need to try to fit
-        int totalArea = 0;
+        // Adjust the Y-offset so that the fonts are top-aligned
+        int scaledAscent = (int)(scale * ascent);
         for (int i = 0; i < fontInfo.numGlyphs; ++i)
         for (int i = 0; i < fontInfo.numGlyphs; ++i)
         {
         {
-            if ((newFace.mGlyphs[i].mWidth) && (newFace.mGlyphs[i].mHeight))
-                totalArea += (newFace.mGlyphs[i].mWidth + 1) * (newFace.mGlyphs[i].mHeight + 1);
+            if (glyphUsed[i])
+                newFace.mGlyphs[i].mOffsetY += scaledAscent;
         }
         }
         
         
-        if (totalArea > texWidth * texHeight)
-            success = false;
-        else
+        // Calculate row advance
+        newFace.mRowHeight = (int)(scale * (ascent - descent + lineGap));
+        
+        // Now try to pack into the smallest possible texture
+        int texWidth = FONT_TEXTURE_MIN_SIZE;
+        int texHeight = FONT_TEXTURE_MIN_SIZE;
+        bool doubleHorizontal = true;
+        for (;;)
         {
         {
-            PROFILE(Font_FitToTexture);
+            bool success = true;
             
             
-            AreaAllocator allocator(texWidth, texHeight);
+            // Check first for theoretical possible fit. If it fails, there is no need to try to fit
+            int totalArea = 0;
             for (int i = 0; i < fontInfo.numGlyphs; ++i)
             for (int i = 0; i < fontInfo.numGlyphs; ++i)
             {
             {
                 if ((newFace.mGlyphs[i].mWidth) && (newFace.mGlyphs[i].mHeight))
                 if ((newFace.mGlyphs[i].mWidth) && (newFace.mGlyphs[i].mHeight))
+                    totalArea += (newFace.mGlyphs[i].mWidth + 1) * (newFace.mGlyphs[i].mHeight + 1);
+            }
+            
+            if (totalArea > texWidth * texHeight)
+                success = false;
+            else
+            {
+                PROFILE(Font_FitToTexture);
+                
+                AreaAllocator allocator(texWidth, texHeight);
+                for (int i = 0; i < fontInfo.numGlyphs; ++i)
                 {
                 {
-                    int x, y;
-                    // Reserve an empty border between glyphs for filtering
-                    if (!allocator.reserve(newFace.mGlyphs[i].mWidth + 1, newFace.mGlyphs[i].mHeight + 1, x, y))
+                    if ((newFace.mGlyphs[i].mWidth) && (newFace.mGlyphs[i].mHeight))
                     {
                     {
-                        success = false;
-                        break;
+                        int x, y;
+                        // Reserve an empty border between glyphs for filtering
+                        if (!allocator.reserve(newFace.mGlyphs[i].mWidth + 1, newFace.mGlyphs[i].mHeight + 1, x, y))
+                        {
+                            success = false;
+                            break;
+                        }
+                        else
+                        {
+                            newFace.mGlyphs[i].mX = x;
+                            newFace.mGlyphs[i].mY = y;
+                        }
                     }
                     }
                     else
                     else
                     {
                     {
-                        newFace.mGlyphs[i].mX = x;
-                        newFace.mGlyphs[i].mY = y;
+                        newFace.mGlyphs[i].mX = 0;
+                        newFace.mGlyphs[i].mY = 0;
                     }
                     }
                 }
                 }
+            }
+            
+            if (!success)
+            {
+                // Alternate between doubling the horizontal and the vertical dimension
+                if (doubleHorizontal)
+                    texWidth <<= 1;
                 else
                 else
-                {
-                    newFace.mGlyphs[i].mX = 0;
-                    newFace.mGlyphs[i].mY = 0;
-                }
+                    texHeight <<= 1;
+                
+                if ((texWidth > FONT_TEXTURE_MAX_SIZE) || (texHeight > FONT_TEXTURE_MAX_SIZE))
+                    EXCEPTION("Font face could not be fit into the largest possible texture");
+                
+                doubleHorizontal = !doubleHorizontal;
             }
             }
+            else
+                break;
         }
         }
         
         
-        if (!success)
+        // Create the texture
+        if (mRenderer)
         {
         {
-            // Alternate between doubling the horizontal and the vertical dimension
-            if (doubleHorizontal)
-                texWidth <<= 1;
-            else
-                texHeight <<= 1;
+            SharedPtr<Texture2D> texture(new Texture2D(mRenderer, TEXTURE_STATIC));
             
             
-            if ((texWidth > FONT_TEXTURE_MAX_SIZE) || (texHeight > FONT_TEXTURE_MAX_SIZE))
-                EXCEPTION("Font face could not be fit into the largest possible texture");
+            texture->setNumLevels(1); // No mipmaps
+            texture->setAddressMode(COORD_U, ADDRESS_BORDER);
+            texture->setAddressMode(COORD_V, ADDRESS_BORDER),
+            texture->setBorderColor(Color(0.0f, 0.0f, 0.0f, 0.0f));
+            texture->setSize(texWidth, texHeight, D3DFMT_A8);
             
             
-            doubleHorizontal = !doubleHorizontal;
-        }
-        else
-            break;
-    }
-    
-    // Create the texture
-    if (mRenderer)
-    {
-        SharedPtr<Texture2D> texture(new Texture2D(mRenderer, TEXTURE_STATIC));
-        
-        texture->setNumLevels(1); // No mipmaps
-        texture->setAddressMode(COORD_U, ADDRESS_BORDER);
-        texture->setAddressMode(COORD_V, ADDRESS_BORDER),
-        texture->setBorderColor(Color(0.0f, 0.0f, 0.0f, 0.0f));
-        texture->setSize(texWidth, texHeight, D3DFMT_A8);
-        
-        D3DLOCKED_RECT hwRect;
-        texture->lock(0, 0, &hwRect);
-        
-        // First clear the whole texture
-        for (int y = 0; y < texHeight; ++y)
-        {
-            unsigned char* dest = (unsigned char*)hwRect.pBits + hwRect.Pitch * y;
-            memset(dest, 0, texWidth);
-        }
-        
-        // Render glyphs into texture, and find out a scaling value in case font uses less than full opacity (thin outlines)
-        int sumOpacity = 0;
-        int nonEmptyGlyphs = 0;
-        for (int i = 0; i < fontInfo.numGlyphs; ++i)
-        {
-            if ((newFace.mGlyphs[i].mWidth) && (newFace.mGlyphs[i].mHeight))
+            D3DLOCKED_RECT hwRect;
+            texture->lock(0, 0, &hwRect);
+            
+            // First clear the whole texture
+            for (int y = 0; y < texHeight; ++y)
             {
             {
-                stbtt_MakeGlyphBitmap(&fontInfo, (unsigned char*)hwRect.pBits + hwRect.Pitch * newFace.mGlyphs[i].mY + newFace.mGlyphs[i].mX, newFace.mGlyphs[i].mWidth, newFace.mGlyphs[i].mHeight, hwRect.Pitch, scale, scale, i);
-                
-                int glyphMaxOpacity = 0;
-                for (int y = 0; y < newFace.mGlyphs[i].mHeight; ++y)
+                unsigned char* dest = (unsigned char*)hwRect.pBits + hwRect.Pitch * y;
+                memset(dest, 0, texWidth);
+            }
+            
+            // Render glyphs into texture, and find out a scaling value in case font uses less than full opacity (thin outlines)
+            int sumOpacity = 0;
+            int nonEmptyGlyphs = 0;
+            for (int i = 0; i < fontInfo.numGlyphs; ++i)
+            {
+                if ((newFace.mGlyphs[i].mWidth) && (newFace.mGlyphs[i].mHeight))
                 {
                 {
-                    unsigned char* pixels = (unsigned char*)hwRect.pBits + hwRect.Pitch * (y + newFace.mGlyphs[i].mY) + newFace.mGlyphs[i].mX;
+                    stbtt_MakeGlyphBitmap(&fontInfo, (unsigned char*)hwRect.pBits + hwRect.Pitch * newFace.mGlyphs[i].mY + newFace.mGlyphs[i].mX, newFace.mGlyphs[i].mWidth, newFace.mGlyphs[i].mHeight, hwRect.Pitch, scale, scale, i);
                     
                     
-                    for (int x = 0; x < newFace.mGlyphs[i].mWidth; ++x)
-                        glyphMaxOpacity = max(glyphMaxOpacity, pixels[x]);
-                }
-                
-                if (glyphMaxOpacity > 0)
-                {
-                    sumOpacity += glyphMaxOpacity;
-                    ++nonEmptyGlyphs;
+                    int glyphMaxOpacity = 0;
+                    for (int y = 0; y < newFace.mGlyphs[i].mHeight; ++y)
+                    {
+                        unsigned char* pixels = (unsigned char*)hwRect.pBits + hwRect.Pitch * (y + newFace.mGlyphs[i].mY) + newFace.mGlyphs[i].mX;
+                        
+                        for (int x = 0; x < newFace.mGlyphs[i].mWidth; ++x)
+                            glyphMaxOpacity = max(glyphMaxOpacity, pixels[x]);
+                    }
+                    
+                    if (glyphMaxOpacity > 0)
+                    {
+                        sumOpacity += glyphMaxOpacity;
+                        ++nonEmptyGlyphs;
+                    }
                 }
                 }
             }
             }
-        }
-        
-        // Apply the scaling if necessary
-        int avgOpacity = nonEmptyGlyphs ? sumOpacity / nonEmptyGlyphs : 255;
-        if (avgOpacity < 255)
-        {
-            float scale = 255.0f / avgOpacity;
             
             
-            for (int i = 0; i < fontInfo.numGlyphs; ++i)
+            // Apply the scaling if necessary
+            int avgOpacity = nonEmptyGlyphs ? sumOpacity / nonEmptyGlyphs : 255;
+            if (avgOpacity < 255)
             {
             {
-                for (int y = 0; y < newFace.mGlyphs[i].mHeight; ++y)
+                float scale = 255.0f / avgOpacity;
+                
+                for (int i = 0; i < fontInfo.numGlyphs; ++i)
                 {
                 {
-                    unsigned char* dest = (unsigned char*)hwRect.pBits + hwRect.Pitch * (y + newFace.mGlyphs[i].mY) + newFace.mGlyphs[i].mX;
-                    for (int x = 0; x < newFace.mGlyphs[i].mWidth; ++x)
+                    for (int y = 0; y < newFace.mGlyphs[i].mHeight; ++y)
                     {
                     {
-                        int pixel = dest[x];
-                        dest[x] = min((int)(pixel * scale), 255);
+                        unsigned char* dest = (unsigned char*)hwRect.pBits + hwRect.Pitch * (y + newFace.mGlyphs[i].mY) + newFace.mGlyphs[i].mX;
+                        for (int x = 0; x < newFace.mGlyphs[i].mWidth; ++x)
+                        {
+                            int pixel = dest[x];
+                            dest[x] = min((int)(pixel * scale), 255);
+                        }
                     }
                     }
                 }
                 }
             }
             }
+            
+            texture->unlock();
+            setMemoryUse(getMemoryUse() + texWidth * texHeight);
+            newFace.mTexture = staticCast<Texture>(texture);
         }
         }
         
         
-        texture->unlock();
-        setMemoryUse(getMemoryUse() + texWidth * texHeight);
-        newFace.mTexture = staticCast<Texture>(texture);
+        mFaces[pointSize] = newFace;
+        return &mFaces[pointSize];
+    }
+    catch (...)
+    {
+        // An error occurred. Store and return an empty font face
+        FontFace emptyFace;
+        FontGlyph emptyGlyph;
+        emptyGlyph.mX = 0;
+        emptyGlyph.mY = 0;
+        emptyGlyph.mWidth = 0;
+        emptyGlyph.mHeight = 0;
+        emptyGlyph.mOffsetX = 0;
+        emptyGlyph.mOffsetY = 0;
+        emptyGlyph.mAdvanceX = 0;
+        emptyFace.mGlyphs.push_back(emptyGlyph);
+        for (unsigned i = 0; i < MAX_FONT_CHARS; ++i)
+            emptyFace.mGlyphIndex[i] = 0;
+        
+        mFaces[pointSize] = emptyFace;
+        return &mFaces[pointSize];
     }
     }
-    
-    mFaces[pointSize] = newFace;
-    return &mFaces[pointSize];
 }
 }
 
 
 std::string getSystemFontDirectory()
 std::string getSystemFontDirectory()

+ 1 - 1
Engine/UI/Font.h

@@ -84,7 +84,7 @@ public:
     //! Load resource. Throw exception on error
     //! Load resource. Throw exception on error
     virtual void load(Deserializer& source, ResourceCache* cache = 0);
     virtual void load(Deserializer& source, ResourceCache* cache = 0);
     
     
-    //! Return font face. Pack and render to a texture if not rendered yet. Throw exception on error
+    //! Return font face. Pack and render to a texture if not rendered yet. Return empty face on error
     const FontFace* getFace(int pointSize);
     const FontFace* getFace(int pointSize);
     
     
 private:
 private:

+ 1 - 12
Engine/UI/Text.cpp

@@ -87,18 +87,7 @@ bool Text::setFont(Font* font, int size)
     
     
     mFont = font;
     mFont = font;
     mFontSize = max(size, 1);
     mFontSize = max(size, 1);
-    
-    // Catch exception if executed from script
-    try
-    {
-        calculateTextSize();
-    }
-    catch (Exception& e)
-    {
-        mFont = 0;
-        SAFE_RETHROW_RET(e, false);
-    }
-    
+    calculateTextSize();
     return true;
     return true;
 }
 }
 
 

+ 7 - 3
Examples/Urho3D/Application.cpp

@@ -46,10 +46,14 @@ Application::Application()
 
 
 Application::~Application()
 Application::~Application()
 {
 {
-    // The scripts hold references to engine subsystems, not releasing them shows up as numerous memory leaks
-    mScriptFile.reset();
-    if (mCache)
+    // The scripts possibly hold references to engine subsystems, not releasing them shows up as numerous memory leaks
+    if (mEngine)
+    {
+        mScriptFile.reset();
         mCache->releaseResources(ShortStringHash("ScriptFile"), true);
         mCache->releaseResources(ShortStringHash("ScriptFile"), true);
+        // Perform a full garbage collection before shutdown
+        mEngine->getScriptEngine()->garbageCollect(true);
+    }
 }
 }
 
 
 void Application::run()
 void Application::run()