Browse Source

Added four-byte identifiers to binary files.
Fixed point light shape texture when used with shadows.
Removed direction from Light. Instead scene nodes now have setDirection(), which sets the node rotation from a vector.

Lasse Öörni 15 years ago
parent
commit
f9ef9957b7

File diff suppressed because it is too large
+ 112 - 112
Bin/Data/GraphicsTestScene.xml


BIN
Bin/Data/Models/Box.mdl


BIN
Bin/Data/Models/CloudPlane.mdl


+ 11 - 5
Bin/Data/Scripts/GraphicsTest.as

@@ -184,7 +184,6 @@ void initScene()
         object.setScale(1.0 + random() * 0.25);
         object.setModel(cache.getResource("Model", "Models/Jack.mdl"));
         object.setMaterial(cache.getResource("Material", "Materials/Jack.xml"));
-        object.setShadowDistance(200.0);
         object.setDrawDistance(300.0);
         
         AnimationState@ anim = object.addAnimationState(cache.getResource("Animation", "Models/Jack_Walk.ani"));
@@ -253,11 +252,16 @@ void initScene()
         light.setSpecularIntensity(1.0f);
         light.setCastShadows(true);
         light.setShadowBias(BiasParameters(0.00002, 0.0));
+        light.setShadowDistance(200.0f);
+        light.setShadowFadeDistance(150.0f);
         light.setShadowResolution(0.5);
         // The spot lights will not have anything near them, so move the near plane of the shadow camera farther
         // for better shadow depth resolution
         light.setShadowNearFarRatio(0.01);
         
+        // Store the original rotation as an entity property
+        newEntity.setProperty("rotation", Variant(light.getRotation()));
+
         lights.push(newEntity);
     }
     
@@ -273,7 +277,7 @@ void animateScene(float timeStep)
     for (uint i = 0; i < lights.size(); ++i)
     {
         Light@ light = lights[i].getComponent("Light");
-        light.setRotation(Quaternion(0, objectangle * 2, 0));
+        light.setRotation(Quaternion(0, objectangle * 2, 0) * lights[i].getProperty("rotation").getQuaternion());
     }
     
     for (uint i = 0; i < animatingObjects.size(); ++i)
@@ -356,8 +360,8 @@ void initUI()
 
 void handleFileSelected(StringHash eventType, VariantMap& eventData)
 {
-	unsubscribeFromEvent(fileSelector, "FileSelected");
-	@fileSelector = null;
+    unsubscribeFromEvent(fileSelector, "FileSelected");
+    @fileSelector = null;
 }
 
 void createCamera()
@@ -373,10 +377,12 @@ void createCamera()
     cameraLight.setColor(Color(2.0, 2.0, 2.0));
     cameraLight.setSpecularIntensity(2.0);
     cameraLight.setCastShadows(true);
+    cameraLight.setShadowDistance(200.0f);
+    cameraLight.setShadowFadeDistance(150.0f);
     cameraLight.setShadowResolution(0.5);
     cameraLight.setShadowFocus(FocusParameters(false, false, false, 0.5, 3.0));
     cameraLight.setRampTexture(cache.getResource("Texture2D", "Textures/RampWide.png"));
-    cameraLight.setSpotTexture(cache.getResource("Texture2D", "Textures/SpotWide.png"));
+    cameraLight.setShapeTexture(cache.getResource("Texture2D", "Textures/SpotWide.png"));
     camera.addChild(cameraLight);
 
     pipeline.setViewport(0, Viewport(testScene, camera));

+ 2 - 2
Bin/Data/TestLevel.xml

@@ -13,10 +13,10 @@
             <fog color="0.2 0.2 0.7 0" start="5000" end="15000" />
         </component>
         <component type="Light" netflags="0">
-            <transform pos="0 0 0" rot="1 0 0 0" scale="1 1 1" />
+            <transform pos="0 0 0" rot="0.888074 0.325058 -0.325058 0" scale="1 1 1" />
             <render castshadows="true" occluder="false" visible="true" />
             <lod drawdistance="0" shadowdistance="0" viewmask="-1" lightmask="-1" fadedistance="0" detail="0" shadowdetail="0" />
-            <light type="directional" direction="-0.57735 -0.57735 0.57735" color="1 1 1 1" specular="0" range="0" fov="30" aspectratio="1" />
+            <light type="directional" color="1 1 1 1" specular="0" range="0" fov="30" aspectratio="1" />
             <shadows fadedistance="0" intensity="0" resolution="1" nearfarratio="0.002" />
             <shadowbias constant="0.00025" slopescaled="0.001" />
             <shadowcascade lambda="0.5" maxrange="5000" faderange="0.2" splits="2" />

+ 9 - 0
Engine/Common/Deserializer.cpp

@@ -218,6 +218,15 @@ std::string Deserializer::readString()
     return ret;
 }
 
+std::string Deserializer::readID()
+{
+    std::string ret;
+    
+    ret.resize(4);
+    read(&ret[0], 4);
+    return ret;
+}
+
 StringHash Deserializer::readStringHash()
 {
     StringHash ret;

+ 2 - 0
Engine/Common/Deserializer.h

@@ -102,6 +102,8 @@ public:
     BoundingBox readBoundingBox();
     //! Read a null-terminated string
     std::string readString();
+    //! Read a four-letter ID
+    std::string readID();
     //! Read a 32-bit StringHash
     StringHash readStringHash();
     //! Read a 16-bit ShortStringHash

+ 3 - 0
Engine/Common/PackageFile.cpp

@@ -37,6 +37,9 @@ PackageFile::PackageFile(const std::string& fileName) :
     registerHash(mFileName);
     
     // Read the directory
+    if (file.readID() != "UPAK")
+        EXCEPTION(file.getName() + " is not a valid package file");
+    
     unsigned numFiles = file.readUInt();
     mChecksum = file.readUInt();
     

+ 7 - 0
Engine/Common/Serializer.cpp

@@ -150,6 +150,13 @@ void Serializer::writeString(const std::string& value)
     write(value.c_str(), value.length() + 1);
 }
 
+void Serializer::writeID(const std::string& value)
+{
+    write(value.c_str(), min((int)value.length(), 4));
+    for (unsigned i = value.length(); i < 4; ++i)
+        writeByte(' ');
+}
+
 void Serializer::writeStringHash(const StringHash& value)
 {
     write(value.getData(), sizeof(unsigned));

+ 2 - 0
Engine/Common/Serializer.h

@@ -92,6 +92,8 @@ public:
     void writeBoundingBox(const BoundingBox& value);
     //! Write a null-terminated string
     void writeString(const std::string& value);
+    //! Write a four-letter ID. If the string is not long enough, spaces will be appended
+    void writeID(const std::string& value);
     //! Write a 32-bit StringHash
     void writeStringHash(const StringHash& value);
     //! Write a 16-bit ShortStringHash

+ 1 - 3
Engine/Engine/RegisterRenderer.cpp

@@ -542,8 +542,8 @@ static void registerLight(asIScriptEngine* engine)
 {
     engine->RegisterEnum("LightType");
     engine->RegisterEnumValue("LightType", "LIGHT_DIRECTIONAL", LIGHT_DIRECTIONAL);
-    engine->RegisterEnumValue("LightType", "LIGHT_POINT", LIGHT_POINT);
     engine->RegisterEnumValue("LightType", "LIGHT_SPOT", LIGHT_SPOT);
+    engine->RegisterEnumValue("LightType", "LIGHT_POINT", LIGHT_POINT);
     
     engine->RegisterObjectType("BiasParameters", sizeof(BiasParameters), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_C);
     engine->RegisterObjectBehaviour("BiasParameters", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructBiasParameters), asCALL_CDECL_OBJLAST);
@@ -573,7 +573,6 @@ static void registerLight(asIScriptEngine* engine)
     
     registerVolumeNode<Light>(engine, "Light");
     engine->RegisterObjectMethod("Light", "void setLightType(LightType)", asMETHOD(Light, setLightType), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Light", "void setDirection(const Vector3& in)", asMETHOD(Light, setDirection), asCALL_THISCALL);
     engine->RegisterObjectMethod("Light", "void setColor(const Color& in)", asMETHOD(Light, setColor), asCALL_THISCALL);
     engine->RegisterObjectMethod("Light", "void setSpecularIntensity(float)", asMETHOD(Light, setSpecularIntensity), asCALL_THISCALL);
     engine->RegisterObjectMethod("Light", "void setRange(float)", asMETHOD(Light, setRange), asCALL_THISCALL);
@@ -591,7 +590,6 @@ static void registerLight(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Light", "void setRampTexture(Texture@+)", asMETHOD(Light, setRampTexture), asCALL_THISCALL);
     engine->RegisterObjectMethod("Light", "void setShapeTexture(Texture@+)", asMETHOD(Light, setShapeTexture), asCALL_THISCALL);
     engine->RegisterObjectMethod("Light", "LightType getLightType() const", asMETHOD(Light, getLightType), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Light", "const Vector3& getDirection() const", asMETHOD(Light, getDirection), asCALL_THISCALL);
     engine->RegisterObjectMethod("Light", "const Color& getColor() const", asMETHOD(Light, getColor), asCALL_THISCALL);
     engine->RegisterObjectMethod("Light", "float getSpecularIntensity() const", asMETHOD(Light, getSpecularIntensity), asCALL_THISCALL);
     engine->RegisterObjectMethod("Light", "bool isNegative() const", asMETHOD(Light, isNegative), asCALL_THISCALL);

+ 3 - 0
Engine/Engine/RegisterTemplates.h

@@ -283,6 +283,7 @@ template <class T> void registerNode(asIScriptEngine* engine, const char* classN
     registerComponent<T>(engine, className);
     engine->RegisterObjectMethod(className, "void setPosition(const Vector3& in)", asMETHODPR(T, setPosition, (const Vector3&), void), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void setRotation(const Quaternion& in)", asMETHODPR(T, setRotation, (const Quaternion&), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "void setDirection(const Vector3& in)", asMETHODPR(T, setDirection, (const Vector3&), void), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void setScale(float)", asMETHODPR(T, setScale, (float), void), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void setScale(const Vector3& in)", asMETHODPR(T, setScale, (const Vector3&), void), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void setTransform(const Vector3& in, const Quaternion& in)", asMETHODPR(T, setTransform, (const Vector3&, const Quaternion&), void), asCALL_THISCALL);
@@ -301,9 +302,11 @@ template <class T> void registerNode(asIScriptEngine* engine, const char* classN
     engine->RegisterObjectMethod(className, "void removeAllChildren(bool)", asMETHODPR(T, removeAllChildren, (bool), void), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "const Vector3& getPosition() const", asMETHODPR(T, getPosition, () const, const Vector3&), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "const Quaternion& getRotation() const", asMETHODPR(T, getRotation, () const, const Quaternion&), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "Vector3 getDirection() const", asMETHODPR(T, getDirection, () const, Vector3), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "const Vector3& getScale() const", asMETHODPR(T, getScale, () const, const Vector3&), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "const Vector3& getWorldPosition()", asMETHODPR(T, getWorldPosition, (), const Vector3&), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "const Quaternion& getWorldRotation()", asMETHODPR(T, getWorldRotation, (), const Quaternion&), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "Vector3 getWorldDirection()", asMETHODPR(T, getWorldDirection, (), Vector3), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "const Vector3& getWorldScale()", asMETHODPR(T, getWorldScale, (), const Vector3&), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "uint getNodeFlags() const", asMETHODPR(T, getNodeFlags, () const, unsigned), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "Node@+ getParent() const", asMETHODPR(T, getParent, () const, Node*), asCALL_THISCALL);

+ 2 - 1
Engine/Physics/CollisionShape.cpp

@@ -135,7 +135,8 @@ void getVertexAndIndexData(const Model* model, unsigned lodLevel, SharedArrayPtr
 TriangleMeshData::TriangleMeshData(const Model* model, bool makeConvexHull, float skinWidth, unsigned lodLevel) :
     mTriMesh(0),
     mVertexCount(0),
-    mIndexCount(0)
+    mIndexCount(0),
+    mSkinWidth(skinWidth)
 {
     if (!model)
         EXCEPTION("Null model for TriangleMeshData");

+ 2 - 0
Engine/Physics/CollisionShape.h

@@ -70,6 +70,8 @@ struct TriangleMeshData : public CollisionGeometryData
     unsigned mVertexCount;
     //! Number of indices
     unsigned mIndexCount;
+    //! Skin width (for convex hulls)
+    float mSkinWidth;
     //! Vertex data
     SharedArrayPtr<Vector3> mVertexData;
     //! Index data

+ 7 - 0
Engine/Renderer/Animation.cpp

@@ -59,6 +59,11 @@ void Animation::load(Deserializer& source, ResourceCache* cache)
 {
     unsigned memoryUse = 0;
     
+    // Check ID
+    if (source.readID() != "UANI")
+        EXCEPTION(source.getName() + " is not a valid animation file");
+    
+    // Read name and length
     mAnimationName = source.readString();
     mAnimationNameHash = StringHash(mAnimationName);
     mLength = source.readFloat();
@@ -99,6 +104,8 @@ void Animation::load(Deserializer& source, ResourceCache* cache)
 
 void Animation::save(Serializer& dest)
 {
+    // Write ID, name and length
+    dest.writeID("UANI");
     dest.writeString(mAnimationName);
     dest.writeFloat(mLength);
     

+ 9 - 7
Engine/Renderer/Batch.cpp

@@ -202,8 +202,7 @@ void Batch::draw(Renderer* renderer) const
         if ((light) && (vs->needParameterUpdate(VSP_SPOTPROJ, light)))
         {
             Matrix4x3 spotView;
-            Quaternion rotation(Vector3::sForward, light->getDirection());
-            spotView.define(light->getWorldPosition(), light->getWorldRotation() * rotation, 1.0f);
+            spotView.define(light->getWorldPosition(), light->getWorldRotation(), 1.0f);
             
             Matrix4 spotProj;
             memset(&spotProj, 0, sizeof(spotProj));
@@ -295,7 +294,7 @@ void Batch::draw(Renderer* renderer) const
         }
         
         if (ps->needParameterUpdate(PSP_LIGHTDIR, light))
-            renderer->setPixelShaderConstant(getPSRegister(PSP_LIGHTDIR), -(light->getWorldRotation() * light->getDirection()));
+            renderer->setPixelShaderConstant(getPSRegister(PSP_LIGHTDIR), -light->getWorldDirection());
         
         if (ps->needParameterUpdate(PSP_LIGHTPOS, light))
             renderer->setPixelShaderConstant(getPSRegister(PSP_LIGHTPOS), light->getWorldPosition() - mCamera->getWorldPosition());
@@ -316,8 +315,12 @@ void Batch::draw(Renderer* renderer) const
         if (ps->needParameterUpdate(PSP_LIGHTVECROT, light))
         {
             Matrix4x3 lightVecRot;
-            Quaternion rotation = light->getWorldRotation() * Quaternion(Vector3::sForward, light->getDirection());
-            lightVecRot.define(Vector3::sZero, rotation, Vector3::sUnity);
+            // Use original light if available (split lights)
+            Light* original = light->getOriginalLight();
+            if (!original)
+                lightVecRot.define(Vector3::sZero, light->getWorldRotation(), Vector3::sUnity);
+            else
+                lightVecRot.define(Vector3::sZero, original->getWorldRotation(), Vector3::sUnity);
             
             renderer->setPixelShaderConstant(getPSRegister(PSP_LIGHTVECROT), lightVecRot);
         }
@@ -377,8 +380,7 @@ void Batch::draw(Renderer* renderer) const
     if ((light) && (ps->needParameterUpdate(PSP_SPOTPROJ, light)))
     {
         Matrix4x3 spotView;
-        Quaternion rotation(Vector3::sForward, light->getDirection());
-        spotView.define(light->getWorldPosition(), light->getWorldRotation() * rotation, 1.0f);
+        spotView.define(light->getWorldPosition(), light->getWorldRotation(), 1.0f);
         
         Matrix4 spotProj;
         memset(&spotProj, 0, sizeof(spotProj));

+ 21 - 44
Engine/Renderer/Light.cpp

@@ -34,7 +34,6 @@
 #include "DebugNew.h"
 
 static LightType DEFAULT_LIGHTTYPE = LIGHT_POINT;
-static const Vector3 DEFAULT_DIRECTION = Vector3::sDown;
 static const float DEFAULT_FOV = 30.0f;
 static const float DEFAULT_CONSTANTBIAS = 0.0001f;
 static const float DEFAULT_SLOPESCALEDBIAS = 0.0001f;
@@ -47,8 +46,8 @@ static const float DEFAULT_SHADOWNEARFARRATIO = 0.002f;
 static const std::string typeNames[] =
 {
     "directional",
-    "point",
     "spot",
+    "point",
     "splitpoint"
 };
 
@@ -75,7 +74,6 @@ void FocusParameters::validate()
 Light::Light(Octant* octant, const std::string& name) :
     VolumeNode(NODE_LIGHT, octant, name),
     mLightType(DEFAULT_LIGHTTYPE),
-    mDirection(DEFAULT_DIRECTION),
     mSpecularIntensity(0.0f),
     mRange(0.0f),
     mFov(DEFAULT_FOV),
@@ -95,7 +93,8 @@ Light::Light(Octant* octant, const std::string& name) :
     mNearFadeRange(M_EPSILON),
     mFarFadeRange(M_EPSILON),
     mFrustumDirty(true),
-    mShadowMap(0)
+    mShadowMap(0),
+    mOriginalLight(0)
 {
 }
 
@@ -110,7 +109,6 @@ void Light::save(Serializer& dest)
     
     // Write Light properties
     dest.writeUByte((unsigned char)mLightType);
-    dest.writeVector3(mDirection);
     dest.writeColor(mColor);
     dest.writeFloat(mSpecularIntensity);
     dest.writeFloat(mRange);
@@ -150,7 +148,6 @@ void Light::load(Deserializer& source, ResourceCache* cache)
     
     // Read Light properties
     mLightType = (LightType)source.readUByte();
-    mDirection = source.readVector3();
     mColor = source.readColor();
     mSpecularIntensity = source.readFloat();
     mRange = source.readFloat();
@@ -205,7 +202,6 @@ void Light::saveXML(XMLElement& dest)
     // Write Light properties
     XMLElement lightElem = dest.createChildElement("light");
     lightElem.setString("type", typeNames[mLightType]);
-    lightElem.setVector3("direction", mDirection);
     lightElem.setColor("color", mColor);
     lightElem.setFloat("specular", mSpecularIntensity);
     lightElem.setFloat("range", mRange);
@@ -255,7 +251,6 @@ void Light::loadXML(const XMLElement& source, ResourceCache* cache)
     // Read Light properties
     XMLElement lightElem = source.getChildElement("light");
     mLightType = (LightType)getIndexFromStringList(lightElem.getStringLower("type"), typeNames, 3, 0);
-    mDirection = lightElem.getVector3("direction");
     mColor = lightElem.getColor("color");
     mSpecularIntensity = lightElem.getFloat("specular");
     mRange = lightElem.getFloat("range");
@@ -320,11 +315,10 @@ bool Light::writeNetUpdate(Serializer& dest, Serializer& destRevision, Deseriali
     unsigned char detailLevels = mDetailLevel | (mShadowDetailLevel << 4);
     checkUByte((unsigned char)mLightType, DEFAULT_LIGHTTYPE, baseRevision, bits, 1);
     checkUByte(detailLevels, 0, baseRevision, bits, 1);
-    checkVector3(mDirection, DEFAULT_DIRECTION, baseRevision, bits, 2);
-    checkColor(mColor, Color(), baseRevision, bits, 4);
-    checkFloat(mSpecularIntensity, 0.0f, baseRevision, bits, 4);
-    checkFloat(mRange, 0.0f, baseRevision, bits, 8);
-    checkFloat(mFov, DEFAULT_FOV, baseRevision, bits, 16);
+    checkColor(mColor, Color(), baseRevision, bits, 2);
+    checkFloat(mSpecularIntensity, 0.0f, baseRevision, bits, 2);
+    checkFloat(mRange, 0.0f, baseRevision, bits, 4);
+    checkFloat(mFov, DEFAULT_FOV, baseRevision, bits, 8);
     checkFloat(mAspectRatio, 1.0f, baseRevision, bits, 16);
     checkFloat(mFadeDistance, 0.0f, baseRevision, bits, 32);
     checkStringHash(getResourceHash(mRampTexture), StringHash(), baseRevision, bits, 64);
@@ -346,12 +340,10 @@ bool Light::writeNetUpdate(Serializer& dest, Serializer& destRevision, Deseriali
     checkFloat(mShadowNearFarRatio, DEFAULT_SHADOWNEARFARRATIO, baseRevision, bits2, 64);
     
     // Send only information necessary for the light type, and shadow data only for shadowed lights
-    if (mLightType == LIGHT_POINT)
-        bits &= ~2;
     if (mLightType == LIGHT_DIRECTIONAL)
-        bits &= ~(8 | 32);
+        bits &= ~(4 | 32);
     if (mLightType != LIGHT_SPOT)
-        bits &= ~16;
+        bits &= ~(8 | 16);
     if (!mCastShadows)
         bits2 = 0;
     
@@ -359,11 +351,10 @@ bool Light::writeNetUpdate(Serializer& dest, Serializer& destRevision, Deseriali
     dest.writeUByte(bits2);
     writeUByteDelta((unsigned char)mLightType, dest, destRevision, bits & 1);
     writeUByteDelta(detailLevels, dest, destRevision, bits & 1);
-    writeVector3Delta(mDirection, dest, destRevision, bits & 2);
-    writeColorDelta(mColor, dest, destRevision, bits & 4);
-    writeFloatDelta(mSpecularIntensity, dest, destRevision, bits & 4);
-    writeFloatDelta(mRange, dest, destRevision, bits & 8);
-    writeFloatDelta(mFov, dest, destRevision, bits & 16);
+    writeColorDelta(mColor, dest, destRevision, bits & 2);
+    writeFloatDelta(mSpecularIntensity, dest, destRevision, bits & 2);
+    writeFloatDelta(mRange, dest, destRevision, bits & 4);
+    writeFloatDelta(mFov, dest, destRevision, bits & 8);
     writeFloatDelta(mAspectRatio, dest, destRevision, bits & 16);
     writeFloatDelta(mFadeDistance, dest, destRevision, bits & 32);
     writeStringHashDelta(getResourceHash(mRampTexture), dest, destRevision, bits & 64);
@@ -405,11 +396,10 @@ void Light::readNetUpdate(Deserializer& source, ResourceCache* cache, const NetU
         mDetailLevel = detailLevels & 15;
         mShadowDetailLevel = detailLevels >> 4;
     }
-    readVector3Delta(mDirection, source, bits & 2);
-    readColorDelta(mColor, source, bits & 4);
-    readFloatDelta(mSpecularIntensity, source, bits & 4);
-    readFloatDelta(mRange, source, bits & 8);
-    readFloatDelta(mFov, source, bits & 16);
+    readColorDelta(mColor, source, bits & 2);
+    readFloatDelta(mSpecularIntensity, source, bits & 2);
+    readFloatDelta(mRange, source, bits & 4);
+    readFloatDelta(mFov, source, bits & 8);
     readFloatDelta(mAspectRatio, source, bits & 16);
     readFloatDelta(mFadeDistance, source, bits & 32);
     if (bits & 64)
@@ -508,16 +498,12 @@ void Light::overrideTransforms(unsigned batchIndex, Camera& camera, const Matrix
         {
             float yScale = tan(mFov * M_DEGTORAD * 0.5f) * mRange;
             float xScale = mAspectRatio * yScale;
-            Quaternion rotation(Vector3::sForward, mDirection);
-            lightModel.define(getWorldPosition(), getWorldRotation() * rotation, Vector3(xScale, yScale, mRange));
+            lightModel.define(getWorldPosition(), getWorldRotation(), Vector3(xScale, yScale, mRange));
         }
         break;
         
     case LIGHT_SPLITPOINT:
-        {
-            Quaternion rotation(Vector3::sForward, mDirection);
-            lightModel.define(getWorldPosition(), getWorldRotation() * rotation, mRange);
-        }
+        lightModel.define(getWorldPosition(), getWorldRotation(), mRange);
         break;
     }
 }
@@ -550,14 +536,6 @@ void Light::setLightType(LightType type)
         markDirty();
 }
 
-void Light::setDirection(const Vector3& direction)
-{
-    mDirection = direction.getNormalized();
-    
-    if (!isDirty())
-        markDirty();
-}
-
 void Light::setColor(const Color& color)
 {
     mColor = color;
@@ -653,6 +631,7 @@ void Light::setShapeTexture(Texture* texture)
 
 void Light::copyFrom(Light* original)
 {
+    mOriginalLight = original;
     setPosition(original->getWorldPosition());
     setRotation(original->getWorldRotation());
     setScale(original->getWorldScale());
@@ -662,7 +641,6 @@ void Light::copyFrom(Light* original)
     mViewMask = original->mViewMask;
     mLightMask = original->mLightMask;
     mDistance = original->mDistance;
-    mDirection = original->mDirection;
     mLightType = original->mLightType;
     mRange = original->mRange;
     mFov = original->mFov;
@@ -691,8 +669,7 @@ const Frustum& Light::getFrustum()
     if (mFrustumDirty)
     {
         Matrix4x3 transform;
-        Quaternion rotation(Vector3::sForward, mDirection);
-        transform.define(getWorldPosition(), getWorldRotation() * rotation, 1.0f);
+        transform.define(getWorldPosition(), getWorldRotation(), 1.0f);
         // Set a small near clip distance, so that the near plane can be calculated
         // Note: this is not necessarily the same near clip as on the actual shadow camera
         mFrustum.define(mFov, mAspectRatio, 1.0f, M_MIN_NEARCLIP, mRange, transform);

+ 7 - 9
Engine/Renderer/Light.h

@@ -37,8 +37,8 @@ class TextureCube;
 enum LightType
 {
     LIGHT_DIRECTIONAL = 0,
-    LIGHT_POINT,
     LIGHT_SPOT,
+    LIGHT_POINT,
     LIGHT_SPLITPOINT
 };
 
@@ -168,8 +168,6 @@ public:
     
     //! Set light type
     void setLightType(LightType type);
-    //! Set direction
-    void setDirection(const Vector3& direction);
     //! Set color
     void setColor(const Color& color);
     //! Set specular intensity
@@ -205,8 +203,6 @@ public:
     
     //! Return light type
     LightType getLightType() const { return mLightType; }
-    //! Return direction
-    const Vector3& getDirection() const { return mDirection; }
     //! Return color
     const Color& getColor() const { return mColor; }
     //! Return specular intensity
@@ -270,6 +266,8 @@ public:
     Camera& getShadowCamera() { return mShadowCamera; }
     //! Return shadow map
     Texture2D* getShadowMap() const { return mShadowMap; }
+    //! Return original light (split lights only)
+    Light* getOriginalLight() const { return mOriginalLight; }
     //! Return volume safety extent of spot or point light
     float getVolumeExtent() const;
     //! Return directional light near or far quad transform
@@ -284,8 +282,6 @@ protected:
 private:
     //! Light type
     LightType mLightType;
-    //! Direction
-    Vector3 mDirection;
     //! Color
     Color mColor;
     //! Specular intensity
@@ -330,12 +326,14 @@ private:
     Frustum mFrustum;
     //! Shadow camera
     Camera mShadowCamera;
-    //! Shadow map
-    Texture2D* mShadowMap;
     //! Range attenuation texture
     SharedPtr<Texture> mRampTexture;
     //! Spotlight attenuation texture
     SharedPtr<Texture> mShapeTexture;
+    //! Shadow map
+    Texture2D* mShadowMap;
+    //! Original light for splitting
+    Light* mOriginalLight;
 };
 
 #endif // RENDERER_LIGHT_H

+ 7 - 0
Engine/Renderer/Model.cpp

@@ -75,6 +75,10 @@ void Model::load(Deserializer& source, ResourceCache* cache)
 {
     PROFILE(Model_Load);
     
+    // Check ID
+    if (source.readID() != "UMDL")
+        EXCEPTION(source.getName() + " is not a valid model file");
+    
     mGeometries.clear();
     mGeometryBoneMappings.clear();
     mMorphs.clear();
@@ -235,6 +239,9 @@ void Model::save(Serializer& dest)
         }
     }
     
+    // Write ID
+    dest.writeID("UMDL");
+    
     // Write vertex buffers
     dest.writeUInt(vertexBuffers.size());
     for (unsigned i = 0; i < vertexBuffers.size(); ++i)

+ 3 - 4
Engine/Renderer/View.cpp

@@ -865,7 +865,7 @@ void View::setupShadowCamera(Light* light, bool shadowOcclusion)
             float extrusionDistance = mCamera->getFarClip();
             
             // Calculate initial position & rotation
-            Vector3 lightWorldDirection = light->getWorldRotation() * light->getDirection();
+            Vector3 lightWorldDirection = light->getWorldDirection();
             Vector3 pos = mCamera->getWorldPosition() - extrusionDistance * lightWorldDirection;
             Quaternion rot(Vector3::sForward, lightWorldDirection);
             shadowCamera.setPosition(pos);
@@ -904,9 +904,8 @@ void View::setupShadowCamera(Light* light, bool shadowOcclusion)
     case LIGHT_SPOT:
     case LIGHT_SPLITPOINT:
         {
-            Quaternion rotation(Vector3::sForward, light->getDirection());
             shadowCamera.setPosition(light->getWorldPosition());
-            shadowCamera.setRotation(light->getWorldRotation() * rotation);
+            shadowCamera.setRotation(light->getWorldRotation());
             shadowCamera.setNearClip(light->getShadowNearFarRatio() * light->getRange());
             shadowCamera.setFarClip(light->getRange());
             shadowCamera.setOrthographic(false);
@@ -1163,7 +1162,7 @@ unsigned View::splitLight(Light* light)
             sSplitLights[i] = splitLight;
             
             splitLight->setLightType(LIGHT_SPLITPOINT);
-            splitLight->setRotation(Quaternion::sIdentity);
+            // When making a shadowed point light, align the splits along X, Y and Z axes regardless of light rotation
             splitLight->setDirection(directions[i]);
             splitLight->setFov(90.0f);
             splitLight->setAspectRatio(1.0f);

+ 5 - 0
Engine/Scene/Node.cpp

@@ -341,6 +341,11 @@ void Node::setRotation(const Quaternion& rotation)
         markDirty();
 }
 
+void Node::setDirection(const Vector3& direction)
+{
+    setRotation(Quaternion(Vector3::sForward, direction));
+}
+
 void Node::setScale(float scale)
 {
     mScale = Vector3(scale, scale, scale);

+ 15 - 2
Engine/Scene/Node.h

@@ -94,6 +94,8 @@ public:
     void setPosition(const Vector3& position);
     //! Set rotation
     void setRotation(const Quaternion& rotation);
+    //! Set direction. Positive Z equals identity
+    void setDirection(const Vector3& direction);
     //! Set uniform scale
     void setScale(float scale);
     //! Set scale
@@ -133,6 +135,8 @@ public:
     const Vector3& getPosition() const { return mPosition; }
     //! Return rotation
     const Quaternion& getRotation() const { return mRotation; }
+    //! Return direction. Identity rotation equals positive Z
+    Vector3 getDirection() const { return mRotation * Vector3::sForward; }
     //! Return scale
     const Vector3& getScale() const { return mScale; }
     
@@ -141,7 +145,7 @@ public:
     {
         if (mDirty)
             updateWorldPosition();
-    
+        
         return mWorldPosition;
     }
     
@@ -150,10 +154,19 @@ public:
     {
         if (mDirty)
             updateWorldPosition();
-    
+        
         return mWorldRotation;
     }
     
+    //! Return world-space direction
+    Vector3 getWorldDirection()
+    {
+        if (mDirty)
+            updateWorldPosition();
+        
+        return mWorldRotation * Vector3::sForward;
+    }
+    
     //! Return world-space scale
     const Vector3& getWorldScale()
     {

+ 8 - 7
Engine/Scene/Scene.cpp

@@ -121,7 +121,8 @@ void Scene::interpolate(float timeStep)
 
 void Scene::save(Serializer& dest)
 {
-    // Write scene name
+    // Write ID and scene name
+    dest.writeID("USCN");
     dest.writeString(mName);
     
     // Write extension properties
@@ -155,15 +156,17 @@ void Scene::load(Deserializer& source)
     if (!mCache)
         return;
     
+    // Check ID
+    if (source.readID() != "USCN")
+        EXCEPTION(source.getName() + " is not a valid binary scene file");
+    
     LOGINFO("Loading scene from " + source.getName());
     
     stopAsyncLoading();
     removeAllEntities();
     
-    // Read scene name
+    // Read scene name and extension properties
     mName = source.readString();
-    
-    // Read extension properties
     loadProperties(source);
     
     // Read entities
@@ -221,10 +224,8 @@ void Scene::loadXML(Deserializer& source)
     stopAsyncLoading();
     removeAllEntities();
     
-    // Read scene name
+    // Read scene name and extension properties
     mName = sceneElem.getString("name");
-    
-    // Read extension properties
     loadPropertiesXML(sceneElem);
     
     // Read entities

+ 4 - 0
Tools/OgreImporter/OgreImporter.cpp

@@ -902,6 +902,9 @@ void writeOutput(const std::string& outputFileName, bool exportAnimations, bool
     // Begin serialization
     File dest(outputFileName, FILE_WRITE);
     
+    // ID
+    dest.writeID("UMDL");
+    
     // Vertexbuffers
     dest.writeUInt(vertexBuffers.size());
     for (unsigned i = 0; i < vertexBuffers.size(); ++i)
@@ -1053,6 +1056,7 @@ void writeOutput(const std::string& outputFileName, bool exportAnimations, bool
                 // Write each animation into a separate file
                 std::string animationFileName = replace(outputFileName, ".mdl", "") + "_" + newAnimation.mName + ".ani";
                 File dest(animationFileName, FILE_WRITE);
+                dest.writeID("UANI");
                 dest.writeString(newAnimation.mName);
                 dest.writeFloat(newAnimation.mLength);
                 dest.writeUInt(newAnimation.mTracks.size());

+ 3 - 1
Tools/PackageTool/PackageTool.cpp

@@ -138,7 +138,8 @@ void writePackageFile(const std::string& fileName, const std::string& rootDir)
     
     File dest(fileName, FILE_WRITE);
     
-    // Write number of files & placeholder for checksum
+    // Write ID, number of files & placeholder for checksum
+    dest.writeID("UPAK");
     dest.writeUInt(gEntries.size());
     dest.writeUInt(gChecksum);
     
@@ -175,6 +176,7 @@ void writePackageFile(const std::string& fileName, const std::string& rootDir)
     
     // Write header again with correct offsets & checksums
     dest.seek(0);
+    dest.writeID("UPAK");
     dest.writeUInt(gEntries.size());
     dest.writeUInt(gChecksum);
     

Some files were not shown because too many files changed in this diff