Browse Source

Fixed numerical stability in Quaternion matrix constructor.
Fixed spotlight wrong size in deferred rendering.
Removed "first light" optimization pass due to buggy interactions with alpha blending.
Fixed morphWeights property naming on AnimatedModel.
Fixed missing shader combinations.
Ported the GraphicsTest example from Urho3D 1.0.

Lasse Öörni 14 years ago
parent
commit
f0ecda9785

+ 0 - 1
Bin/CoreData/Techniques/Diff.xml

@@ -1,7 +1,6 @@
 <technique>
     <pass name="gbuffer" vs="GBuffer" ps="GBuffer_Diff" />
     <pass name="base" vs="Forward" ps="Forward_DiffAmbient" />
-    <pass name="litbase" vs="Forward" ps="Forward_DiffAmbient" />
     <pass name="light" vs="Forward" ps="Forward_Diff" depthwrite="false" blend="add" />
     <pass name="shadow" vs="Shadow" ps="Shadow" />
 </technique>

+ 0 - 1
Bin/CoreData/Techniques/DiffAlpha.xml

@@ -1,6 +1,5 @@
 <technique>
     <pass name="base" vs="Forward" ps="Forward_DiffAmbient" depthwrite="false" blend="alpha" />
-    <pass name="litbase" vs="Forward" ps="Forward_DiffAmbient" depthwrite="false" blend="alpha" />
     <pass name="light" vs="Forward" ps="Forward_Diff" depthwrite="false" blend="addalpha" />
     <pass name="shadow" vs="Shadow" ps="Shadow" />
 </technique>

+ 0 - 1
Bin/CoreData/Techniques/DiffAlphaMask.xml

@@ -1,7 +1,6 @@
 <technique>
     <pass name="gbuffer" vs="GBuffer" ps="GBuffer_DiffMask" alphamask="true" />
     <pass name="base" vs="Forward" ps="Forward_DiffAmbient" alphatest="true" />
-    <pass name="litbase" vs="Forward" ps="Forward_DiffAmbient" alphatest="true" />
     <pass name="light" vs="Forward" ps="Forward_Diff" alphatest="true" depthwrite="false" blend="add" />
     <pass name="shadow" vs="Shadow_Mask" ps="Shadow_Mask" alphamask="true" />
 </technique>

+ 0 - 1
Bin/CoreData/Techniques/DiffNormal.xml

@@ -1,7 +1,6 @@
 <technique>
     <pass name="gbuffer" vs="GBuffer_Normal" ps="GBuffer_DiffNormal" />
     <pass name="base" vs="Forward" ps="Forward_DiffAmbient" />
-    <pass name="litbase" vs="Forward_Normal" ps="Forward_DiffNormalAmbient" />
     <pass name="light" vs="Forward_Normal" ps="Forward_DiffNormal" depthwrite="false" blend="add" />
     <pass name="shadow" vs="Shadow" ps="Shadow" />
 </technique>

+ 0 - 1
Bin/CoreData/Techniques/DiffNormalAlpha.xml

@@ -1,6 +1,5 @@
 <technique>
     <pass name="base" vs="Forward" ps="Forward_DiffAmbient" depthwrite="false" blend="alpha" />
-    <pass name="litbase" vs="Forward_Normal" ps="Forward_DiffNormalAmbient" depthwrite="false" blend="alpha" />
     <pass name="light" vs="Forward_Normal" ps="Forward_DiffNormal" depthwrite="false" blend="addalpha" />
     <pass name="shadow" vs="Shadow" ps="Shadow" />
 </technique>

+ 0 - 1
Bin/CoreData/Techniques/DiffNormalAlphaMask.xml

@@ -1,6 +1,5 @@
 <technique>
     <pass name="base" vs="Forward" ps="Forward_DiffAmbient" alphatest="true" />
-    <pass name="litbase" vs="Forward_Normal" ps="Forward_DiffNormalAmbient" alphatest="true" />
     <pass name="light" vs="Forward_Normal" ps="Forward_DiffNormal" alphatest="true" depthwrite="false" blend="add" />
     <pass name="shadow" vs="Shadow_Mask" ps="Shadow_Mask" alphamask="true" />
 </technique>

+ 5 - 0
Bin/CoreData/Techniques/DiffVolumeAlpha.xml

@@ -0,0 +1,5 @@
+<technique>
+    <pass name="base" vs="Forward" ps="Forward_DiffAmbient" depthwrite="false" blend="alpha" />
+    <pass name="light" vs="Forward" ps="Forward_DiffVolume" depthwrite="false" blend="addalpha" />
+    <pass name="shadow" vs="Shadow" ps="Shadow" />
+</technique>

+ 0 - 1
Bin/CoreData/Techniques/NoTexture.xml

@@ -1,7 +1,6 @@
 <technique>
     <pass name="gbuffer" vs="GBuffer" ps="GBuffer" />
     <pass name="base" vs="Forward" ps="Forward_Ambient" />
-    <pass name="litbase" vs="Forward" ps="Forward_Ambient" />
     <pass name="light" vs="Forward" ps="Forward" depthwrite="false" blend="add" />
     <pass name="shadow" vs="Shadow" ps="Shadow" />
 </technique>

+ 0 - 1
Bin/CoreData/Techniques/NoTextureAlpha.xml

@@ -1,5 +1,4 @@
 <technique>
     <pass name="base" vs="Forward" ps="Forward_Ambient" depthwrite="false" blend="alpha" />
-    <pass name="litbase" vs="Forward" ps="Forward_Ambient" depthwrite="false" blend="alpha" />
     <pass name="light" vs="Forward" ps="Forward" depthwrite="false" blend="addalpha" />
 </technique>

+ 5 - 0
Bin/Data/Materials/LitSmoke.xml

@@ -0,0 +1,5 @@
+<material>
+    <technique name="Techniques/DiffVolumeAlpha.xml" />
+    <parameter name="MatDiffColor" value="0.5 0.5 0.5 0.5" />    
+    <texture unit="diffuse" name="Textures/Smoke.dds" />
+</material>

+ 563 - 0
Bin/Data/Scripts/TestSceneOld.as

@@ -0,0 +1,563 @@
+// GraphicsTest rewrite from Urho3D 1.0
+
+const uint NUM_OBJECTS = 250;
+const uint NUM_LIGHTS = 20;
+const uint NUM_INSTANCENODES = 20;
+const uint NUM_INSTANCES = 50;
+const uint NUM_BILLBOARDNODES = 20;
+const uint NUM_BILLBOARDS = 15;
+
+Scene@ testScene;
+Node@ cameraNode;
+Camera@ camera;
+Node@ cameraLightNode;
+Light@ cameraLight;
+float yaw = 0.0;
+float pitch = 0.0;
+float objectangle = 0.0;
+bool paused = true;
+int drawDebug = 0;
+
+Array<Node@> animatingObjects;
+Array<Node@> billboards;
+Array<Node@> lights;
+
+void Start()
+{
+    if (engine.headless)
+    {
+        ErrorDialog("GraphicsTest", "Headless mode is not supported. The program will now exit.");
+        engine.Exit();
+        return;
+    }
+         
+    InitConsole();
+    InitScene();
+    InitUI();
+    CreateCamera();
+
+    SubscribeToEvent("Update", "HandleUpdate");
+    SubscribeToEvent("KeyDown", "HandleKeyDown");
+    SubscribeToEvent("MouseMove", "HandleMouseMove");
+    SubscribeToEvent("MouseButtonDown", "HandleMouseButtonDown");
+    SubscribeToEvent("MouseButtonUp", "HandleMouseButtonUp");
+    SubscribeToEvent("PostRenderUpdate", "HandlePostRenderUpdate");
+}
+
+void InitScene()
+{
+    testScene = Scene("GraphicsTest");
+
+    // Make the scene directly accessible from the console
+    script.defaultScene = testScene;
+
+    testScene.CreateComponent("Octree");
+    testScene.CreateComponent("DebugRenderer");
+    PhysicsWorld@ world = testScene.CreateComponent("PhysicsWorld");
+
+    // Set the physics world parameters
+    world.gravity = Vector3(0, -9.81, 0);
+    world.fps = 100;
+    world.linearRestThreshold = 0.1;
+    world.angularRestThreshold = 0.1;
+    world.contactSurfaceLayer = 0.001;
+
+    // Create the directional light
+    Node@ sunNode = testScene.CreateChild();
+    sunNode.direction = Vector3(0.5, -1, 0.5);
+    Light@ sunLight = sunNode.CreateComponent("Light");
+    sunLight.lightType = LIGHT_DIRECTIONAL;
+    sunLight.color = Color(0.2, 0.2, 0.2);
+    sunLight.specularIntensity = 1;
+
+    // Create a zone to control the ambient lighting
+    Node@ zoneNode = testScene.CreateChild();
+    Zone@ zone = zoneNode.CreateComponent("Zone");
+    zone.boundingBox = BoundingBox(-1000, 1000);
+    zone.ambientColor = Color(0.1, 0.1, 0.1);
+
+    // Create the "floor"
+    for (int y = -5; y <= 5; y++)
+    {
+        for (int x = -5; x <= 5; x++)
+        {
+            Node@ newNode = testScene.CreateChild();
+            newNode.position = Vector3(x * 20.5, -0.5, y * 20.5);
+            newNode.scale = Vector3(10, 0.5, 10);
+
+            CollisionShape@ shape = newNode.CreateComponent("CollisionShape");
+            shape.SetBox(Vector3(1, 1, 1));
+            shape.collisionLayer = 2;
+            shape.collisionMask = 1;
+            
+            StaticModel@ object = newNode.CreateComponent("StaticModel");
+            object.model = cache.GetResource("Model", "Models/Box.mdl");
+            object.material = cache.GetResource("Material", "Materials/Test.xml");
+        }
+    }
+
+    // Create 2 occluder walls
+    for (int x = 0; x < 2; x++)
+    {
+        Node@ newNode = testScene.CreateChild();
+        newNode.position = Vector3(0, 5, 0);
+        newNode.rotation = Quaternion(x * 90, Vector3(0, 1, 0));
+        newNode.scale = Vector3(112, 5, 0.5);
+
+        CollisionShape@ shape = newNode.CreateComponent("CollisionShape");
+        shape.SetBox(Vector3(1, 1, 1));
+        shape.collisionLayer = 2;
+        shape.collisionMask = 1;
+
+        StaticModel@ object = newNode.CreateComponent("StaticModel");
+        object.model = cache.GetResource("Model", "Models/Box.mdl");
+        object.material = cache.GetResource("Material", "Materials/Test.xml");
+        object.castShadows = true;
+        object.occluder = true;
+    }
+    
+    // Create static mushroom with physics
+    {
+        Node@ newNode = testScene.CreateChild();
+        newNode.position = Vector3(50, 0, 50);
+        newNode.SetScale(10);
+
+        CollisionShape@ shape = newNode.CreateComponent("CollisionShape");
+        shape.SetTriangleMesh(cache.GetResource("CollisionShape", "Physics/Mushroom.xml"), 0);
+        shape.collisionLayer = 2;
+        shape.collisionMask = 1;
+
+        StaticModel@ object = newNode.CreateComponent("StaticModel");
+        object.model = cache.GetResource("Model", "Models/Mushroom.mdl");
+        object.material = cache.GetResource("Material", "Materials/Mushroom.xml");
+        object.castShadows = true;
+        object.occluder = true;
+    }
+    
+    // Create mushroom groups
+    for (uint i = 0; i < NUM_INSTANCENODES; ++i)
+    {
+        Node@ newNode = testScene.CreateChild();
+        newNode.position = Vector3(Random() * 160 - 80, 0, Random() * 160 - 80);
+        
+        for (uint j = 0; j < NUM_INSTANCES; ++j)
+        {
+            float angle = Random() * 360;
+            float size = 1 + Random() * 2;
+            
+            Node@ instance = newNode.CreateChild();
+            instance.position = Vector3(Random() * 20 - 10, 0, Random() * 20 - 10);
+            instance.rotation = Quaternion(angle, Vector3(0, 1, 0));
+            instance.SetScale(size);
+
+            StaticModel@ object = instance.CreateComponent("StaticModel");
+            object.model = cache.GetResource("Model", "Models/Mushroom.mdl");
+            object.material = cache.GetResource("Material", "Materials/Mushroom.xml");
+            object.castShadows = true;
+        }
+    }
+
+    // Create animated models
+    for (uint i = 0; i < NUM_OBJECTS; ++i)
+    {
+        Node@ newNode = testScene.CreateChild();
+        newNode.position = Vector3(Random() * 180 - 90, 0, Random() * 180 - 90);
+        newNode.rotation = Quaternion(Random() * 360, Vector3(0, 1, 0));
+        newNode.SetScale(1 + Random() * 0.25);
+
+        AnimatedModel@ object = newNode.CreateComponent("AnimatedModel");
+        object.model = cache.GetResource("Model", "Models/Jack.mdl");
+        object.material = cache.GetResource("Material", "Materials/Jack.xml");
+        object.drawDistance = 300;
+        object.castShadows = true;
+
+        AnimationState@ anim = object.AddAnimationState(cache.GetResource("Animation", "Models/Jack_Walk.ani"));
+        anim.useNlerp = true;
+        anim.looped = true;
+        anim.weight = 1;
+
+        animatingObjects.Push(newNode);
+    }
+
+    // Create floating smoke clouds
+    for (uint i = 0; i < NUM_BILLBOARDNODES; ++i)
+    {
+        Node@ newNode = testScene.CreateChild();
+        newNode.position = Vector3(Random() * 200 - 100, Random() * 15 + 5, Random() * 200 - 100);
+
+        BillboardSet@ billboard = newNode.CreateComponent("BillboardSet");
+        billboard.numBillboards = NUM_BILLBOARDS;
+        billboard.material = cache.GetResource("Material", "Materials/LitSmoke.xml");
+        billboard.sorted = true;
+
+        for (uint j = 0; j < NUM_BILLBOARDS; ++j)
+        {
+            Billboard@ bb = billboard.billboards[j];
+            bb.position = Vector3(Random() * 15 - 7.5, Random() * 8 - 4, Random() * 15 - 7.5);
+            bb.size = Vector2(Random() * 2 + 3, Random() * 2 + 3);
+            bb.rotation = Random() * 360;
+            bb.enabled = true;
+        }
+        billboard.Updated();
+
+        billboards.Push(newNode);
+    }
+
+    // Create lights
+    for (uint i = 0; i < NUM_LIGHTS; ++i)
+    {
+        Node@ newNode = testScene.CreateChild();
+        Light@ light = newNode.CreateComponent("Light");
+
+        Vector3 position(
+            Random() * 150 - 75,
+            Random() * 30 + 30,
+            Random() * 150 - 75
+        );
+
+        Color color(
+            (RandomInt() & 1) * 0.5 + 0.5,
+            (RandomInt() & 1) * 0.5 + 0.5,
+            (RandomInt() & 1) * 0.5 + 0.5
+        );
+
+        if ((color.r == 0.5) && (color.g == 0.5) && (color.b == 0.5))
+            color = Color(1, 1, 1);
+
+        float angle = Random() * 360;
+
+        newNode.position = position;
+        newNode.direction = Vector3(Sin(angle), -1, Cos(angle));
+        light.lightType = LIGHT_SPOT;
+        light.range = 75;
+        light.rampTexture = cache.GetResource("Texture2D", "Textures/RampExtreme.png");
+        light.fov = 15;
+        light.color = color;
+        light.specularIntensity = 1;
+        light.castShadows = true;
+        light.shadowBias = BiasParameters(0.00002, 0.0);
+        light.shadowDistance = 200;
+        light.shadowFadeDistance = 150;
+        light.shadowResolution = 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.shadowNearFarRatio = 0.01;
+
+        // Store the original rotation as an entity property
+        newNode.vars["rotation"] = Variant(newNode.rotation);
+
+        lights.Push(newNode);
+    }
+
+    // Save the ready scene for examination
+    testScene.SaveXML(File("Data/SceneOld.xml", FILE_WRITE));
+}
+
+void AnimateScene(float timeStep)
+{
+    objectangle += 10 * timeStep;
+
+    for (uint i = 0; i < lights.length; ++i)
+        lights[i].rotation = Quaternion(0, objectangle * 2, 0) * lights[i].vars["rotation"].GetQuaternion();
+
+    for (uint i = 0; i < animatingObjects.length; ++i)
+    {
+        AnimatedModel@ model = animatingObjects[i].GetComponent("AnimatedModel");
+        model.animationStates["Walk"].AddTime(timeStep);
+    }
+
+    for (uint i = 0; i < billboards.length; ++i)
+    {
+        BillboardSet@ billboard = billboards[i].GetComponent("BillboardSet");
+        for (uint j = 0; j < billboard.numBillboards; ++j)
+            billboard.billboards[j].rotation += 50 * timeStep;
+        billboard.Updated();
+    }
+}
+
+void InitConsole()
+{
+    XMLFile@ uiStyle = cache.GetResource("XMLFile", "UI/DefaultStyle.xml");
+
+    Console@ console = engine.CreateConsole();
+    console.style = uiStyle;
+    console.numRows = 16;
+
+    DebugHud@ hud = engine.CreateDebugHud();
+    debugHud.style = uiStyle;
+    debugHud.mode = DEBUGHUD_SHOW_STATS | DEBUGHUD_SHOW_MODE;
+}
+
+void InitUI()
+{
+    XMLFile@ uiStyle = cache.GetResource("XMLFile", "UI/DefaultStyle.xml");
+
+    Cursor@ cursor = Cursor("Cursor");
+    cursor.style = uiStyle;
+    cursor.position = IntVector2(graphics.width / 2, graphics.height / 2);
+    ui.cursor = cursor;
+}
+
+void CreateCamera()
+{
+    cameraNode = testScene.CreateChild("Camera");
+    camera = cameraNode.CreateComponent("Camera");
+    cameraNode.position = Vector3(-50, 2, -50);
+
+    cameraLightNode = cameraNode.CreateChild("CameraLight");
+    cameraLight = cameraLightNode.CreateComponent("Light");
+    cameraLight.lightType = LIGHT_SPOT;
+    cameraLight.range = 50;
+    cameraLight.color = Color(2, 2, 2);
+    cameraLight.specularIntensity = 2;
+    cameraLight.castShadows = true;
+    cameraLight.shadowDistance = 200;
+    cameraLight.shadowFadeDistance = 150;
+    cameraLight.shadowResolution = 0.5;
+    cameraLight.rampTexture = cache.GetResource("Texture2D", "Textures/RampWide.png");
+    cameraLight.shapeTexture = cache.GetResource("Texture2D", "Textures/SpotWide.png");
+
+    renderer.viewports[0] = Viewport(testScene, camera);
+}
+
+void HandleUpdate(StringHash eventType, VariantMap& eventData)
+{
+    float timeStep = eventData["TimeStep"].GetFloat();
+
+    if (!paused)
+        AnimateScene(timeStep);
+
+    if (ui.focusElement is null)
+    {
+        float speedMultiplier = 1.0;
+        if (input.keyDown[KEY_LSHIFT])
+            speedMultiplier = 5.0;
+        if (input.keyDown[KEY_LCTRL])
+            speedMultiplier = 0.1;
+
+        if (input.keyDown['W'])
+            cameraNode.TranslateRelative(Vector3(0, 0, 10) * timeStep * speedMultiplier);
+        if (input.keyDown['S'])
+            cameraNode.TranslateRelative(Vector3(0, 0, -10) * timeStep * speedMultiplier);
+        if (input.keyDown['A'])
+            cameraNode.TranslateRelative(Vector3(-10, 0, 0) * timeStep * speedMultiplier);
+        if (input.keyDown['D'])
+            cameraNode.TranslateRelative(Vector3(10, 0, 0) * timeStep * speedMultiplier);
+
+        if (input.keyPress['1'])
+        {
+            int nextRenderMode = graphics.renderMode;
+            if (input.keyDown[KEY_LSHIFT])
+            {
+                --nextRenderMode;
+                if (nextRenderMode < 0)
+                    nextRenderMode = 1;
+            }
+            else
+            {
+                ++nextRenderMode;
+                if (nextRenderMode > 1)
+                    nextRenderMode = 0;
+            }
+
+            graphics.SetMode(RenderMode(nextRenderMode));
+        }
+
+        if (input.keyPress['2'])
+        {
+            int quality = renderer.textureQuality;
+            ++quality;
+            if (quality > 2)
+                quality = 0;
+            renderer.textureQuality = quality;
+        }
+
+        if (input.keyPress['3'])
+        {
+            int quality = renderer.materialQuality;
+            ++quality;
+            if (quality > 2)
+                quality = 0;
+            renderer.materialQuality = quality;
+        }
+
+        if (input.keyPress['4'])
+            renderer.specularLighting = !renderer.specularLighting;
+
+        if (input.keyPress['5'])
+            renderer.drawShadows = !renderer.drawShadows;
+
+        if (input.keyPress['6'])
+        {
+            int size = renderer.shadowMapSize;
+            size *= 2;
+            if (size > 2048)
+                size = 512;
+            renderer.shadowMapSize = size;
+        }
+
+        if (input.keyPress['7'])
+            renderer.shadowMapHiresDepth = !renderer.shadowMapHiresDepth;
+
+        if (input.keyPress['8'])
+        {
+            bool occlusion = renderer.maxOccluderTriangles > 0;
+            occlusion = !occlusion;
+            renderer.maxOccluderTriangles = occlusion ? 5000 : 0;
+        }
+        
+        if (input.keyPress['9'])
+            renderer.dynamicInstancing = !renderer.dynamicInstancing;
+
+        if (input.keyPress[' '])
+        {
+            drawDebug++;
+            if (drawDebug > 2)
+                drawDebug = 0;
+        }
+
+        if (input.keyPress['P'])
+            paused = !paused;
+            
+        if (input.keyPress['L'])
+        {
+            if (cameraLightNode.parent is testScene)
+            {
+                cameraLightNode.position = Vector3(0, 0, 0);
+                cameraLightNode.rotation = Quaternion();
+                cameraLightNode.parent = cameraNode;
+            }
+            else
+            {
+                cameraLightNode.position = cameraNode.position;
+                cameraLightNode.rotation = cameraNode.rotation;
+                cameraLightNode.parent = testScene;
+            }
+        }
+
+        if (input.keyPress['C'])
+            camera.orthographic = !camera.orthographic;
+
+        if (input.keyPress['T'])
+            debugHud.Toggle(DEBUGHUD_SHOW_PROFILER);
+
+        if (input.keyPress[KEY_F5])
+        {
+            File@ xmlFile = File("Data/SceneOld.xml", FILE_WRITE);
+            testScene.SaveXML(xmlFile);
+        }
+
+        if (input.keyPress[KEY_F7])
+        {
+            File@ xmlFile = File("Data/SceneOld.xml", FILE_READ);
+            if (xmlFile.open)
+            {
+                testScene.LoadXML(xmlFile);
+                // Reacquire camera, as it is part of the scene
+                cameraNode = testScene.GetChild("Camera", true);
+                cameraLightNode = cameraNode.GetChild("CameraLight");
+            }
+        }
+    }
+
+    if (input.keyPress[KEY_ESC])
+    {
+        if (ui.focusElement is null)
+            engine.Exit();
+        else
+            console.visible = false;
+    }
+}
+
+void HandleKeyDown(StringHash eventType, VariantMap& eventData)
+{
+    // Check for toggling the console
+    if (eventData["Key"].GetInt() == KEY_F1)
+        console.Toggle();
+}
+
+void HandleMouseMove(StringHash eventType, VariantMap& eventData)
+{
+    if (eventData["Buttons"].GetInt() & MOUSEB_RIGHT != 0)
+    {
+        int mousedx = eventData["DX"].GetInt();
+        int mousedy = eventData["DY"].GetInt();
+        yaw += mousedx / 10.0f;
+        pitch += mousedy / 10.0f;
+        if (pitch < -90.0f)
+            pitch = -90.0f;
+        if (pitch > 90.0f)
+            pitch = 90.0f;
+
+        cameraNode.rotation = Quaternion(pitch, yaw, 0);
+    }
+}
+
+void HandleMouseButtonDown(StringHash eventType, VariantMap& eventData)
+{
+    int button = eventData["Button"].GetInt();
+    if (button == MOUSEB_RIGHT)
+        ui.cursor.visible = false;
+
+    // Test creating a new physics object
+    if (button == MOUSEB_LEFT && ui.GetElementAt(ui.cursorPosition, true) is null && ui.focusElement is null)
+    {
+        Vector3 position = eventData["Pos"].GetVector3();
+        Quaternion rotation = eventData["Rot"].GetQuaternion();
+    
+        Node@ newNode = testScene.CreateChild();
+        newNode.position = position;
+        newNode.rotation = rotation;
+        newNode.SetScale(0.1);
+    
+        CollisionShape@ shape = newNode.CreateComponent("CollisionShape");
+        shape.SetBox(Vector3(2, 2, 2));
+        shape.friction = 1.0;
+        shape.collisionLayer = 1;
+        shape.collisionMask = 3;
+    
+        RigidBody@ body = newNode.CreateComponent("RigidBody");
+        body.angularMaxVelocity = 500.0;
+        body.linearVelocity = rotation * Vector3(0.0, 1.0, 10.0);
+        body.mass = 1;
+    
+        StaticModel@ object = newNode.CreateComponent("StaticModel");
+        object.model = cache.GetResource("Model", "Models/Box.mdl");
+        object.material = cache.GetResource("Material", "Materials/Test.xml");
+        object.castShadows = true;
+        object.shadowDistance = 150.0;
+        object.drawDistance = 200.0;
+    }
+}
+
+void HandleMouseButtonUp(StringHash eventType, VariantMap& eventData)
+{
+    if (eventData["Button"].GetInt() == MOUSEB_RIGHT)
+        ui.cursor.visible = true;
+}
+
+void HandlePostRenderUpdate()
+{
+    if (engine.headless)
+        return;
+
+    // Draw rendering debug geometry without depth test to see the effect of occlusion
+    if (drawDebug == 1)
+        renderer.DrawDebugGeometry(false);
+    if (drawDebug == 2)
+        testScene.physicsWorld.DrawDebugGeometry(true);
+
+    IntVector2 pos = ui.cursorPosition;
+    if (ui.GetElementAt(pos, true) is null && testScene.octree !is null)
+    {
+        Ray cameraRay = camera.GetScreenRay(float(pos.x) / graphics.width, float(pos.y) / graphics.height);
+        Array<RayQueryResult> result = testScene.octree.Raycast(cameraRay, RAY_TRIANGLE, 250.0, DRAWABLE_GEOMETRY);
+        if (result.length > 0)
+        {
+            Drawable@ object = result[0].drawable;
+            Vector3 rayHitPos = cameraRay.origin + cameraRay.direction * result[0].distance;
+            testScene.debugRenderer.AddBoundingBox(BoundingBox(rayHitPos + Vector3(-0.01, -0.01, -0.01), rayHitPos +
+                Vector3(0.01, 0.01, 0.01)), Color(1.0, 1.0, 1.0), true);
+        }
+    }
+}

+ 1 - 0
Bin/TestSceneOld.bat

@@ -0,0 +1 @@
+Urho3D.exe Scripts/TestSceneOld.as %1 %2 %3 %4 %5 %6 %7 %8

+ 2 - 0
Docs/GettingStarted.dox

@@ -57,6 +57,8 @@ T           Toggle profiling display
 
 TestScene also includes a network replication test, where clients can connect, move around as invisible cameras, and create new physics objects. For this, a server needs to be started with the command TestScene.bat server (-headless switch can optionally given so that the server will not open a graphics window) and clients can connect by specifying the server address on the command line, for example TestScene.bat 127.0.0.1
 
+There is also a variation of TestScene ported from Urho3D 1.0, TestSceneOld. It lacks networking features, but is provided for examining backward compatibility and performance. It can be run with TestSceneOld.bat or by using the command Urho3D.exe Scripts/TestSceneOld.as
+ 
 \section Running_NinjaSnowWar NinjaSnowWar
 
 A third-person action game. To start, run NinjaSnowWar.bat in the Bin directory, or use the command Urho3D.exe Scripts/NinjaSnowWar.as

+ 1 - 1
Docs/ScriptAPI.dox

@@ -1750,7 +1750,6 @@ Methods:<br>
 - float GetMorphWeight(uint) const
 - AnimationState@ GetAnimationState(Animation@) const
 - AnimationState@ GetAnimationState(uint) const
-- float getMorphWeight(const String&) const
 
 Properties:<br>
 - ShortStringHash type (readonly)
@@ -1781,6 +1780,7 @@ Properties:<br>
 - uint numAnimationStates (readonly)
 - AnimationState@[] animationStates (readonly)
 - uint numMorphs (readonly)
+- float[] morphWeights
 
 
 AnimationController

+ 2 - 3
Engine/Engine/GraphicsAPI.cpp

@@ -274,7 +274,6 @@ static void RegisterMaterial(asIScriptEngine* engine)
     engine->RegisterEnum("PassType");
     engine->RegisterEnumValue("PassType", "PASS_GBUFFER", PASS_GBUFFER);
     engine->RegisterEnumValue("PassType", "PASS_BASE", PASS_BASE);
-    engine->RegisterEnumValue("PassType", "PASS_LITBASE", PASS_LITBASE);
     engine->RegisterEnumValue("PassType", "PASS_LIGHT", PASS_LIGHT);
     engine->RegisterEnumValue("PassType", "PASS_EXTRA", PASS_EXTRA);
     engine->RegisterEnumValue("PassType", "PASS_SHADOW", PASS_SHADOW);
@@ -590,8 +589,8 @@ static void RegisterAnimatedModel(asIScriptEngine* engine)
     engine->RegisterObjectMethod("AnimatedModel", "uint get_numAnimationStates() const", asMETHOD(AnimatedModel, GetNumAnimationStates), asCALL_THISCALL);
     engine->RegisterObjectMethod("AnimatedModel", "AnimationState@+ get_animationStates(const String&in) const", asMETHODPR(AnimatedModel, GetAnimationState, (const String&) const, AnimationState*), asCALL_THISCALL);
     engine->RegisterObjectMethod("AnimatedModel", "uint get_numMorphs() const", asMETHOD(AnimatedModel, GetNumMorphs), asCALL_THISCALL);
-    engine->RegisterObjectMethod("AnimatedModel", "void set_morphWeight(const String&in, float)", asMETHODPR(AnimatedModel, SetMorphWeight, (const String&, float), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod("AnimatedModel", "float getMorphWeight(const String&in) const", asMETHODPR(AnimatedModel, GetMorphWeight, (const String&) const, float), asCALL_THISCALL);
+    engine->RegisterObjectMethod("AnimatedModel", "void set_morphWeights(const String&in, float)", asMETHODPR(AnimatedModel, SetMorphWeight, (const String&, float), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("AnimatedModel", "float get_morphWeights(const String&in) const", asMETHODPR(AnimatedModel, GetMorphWeight, (const String&) const, float), asCALL_THISCALL);
 }
 
 static void RegisterAnimationController(asIScriptEngine* engine)

+ 5 - 6
Engine/Graphics/Batch.cpp

@@ -154,8 +154,7 @@ void Batch::Prepare(Graphics* graphics, const HashMap<StringHash, Vector4>& shad
     {
         if (graphics->NeedParameterUpdate(VSP_SPOTPROJ, light_))
         {
-            const Matrix3x4& transform = light_->GetWorldTransform();
-            Matrix3x4 spotView(transform.Translation(), transform.Rotation(), 1.0f);
+            Matrix3x4 spotView(light_->GetWorldPosition(), light_->GetWorldRotation(), 1.0f);
             Matrix4 spotProj(Matrix4::ZERO);
             Matrix4 texAdjust(Matrix4::IDENTITY);
             
@@ -180,8 +179,8 @@ void Batch::Prepare(Graphics* graphics, const HashMap<StringHash, Vector4>& shad
         
         if (graphics->NeedParameterUpdate(PSP_LIGHTATTEN, light_))
         {
-            Vector4 light_Atten(1.0f / Max(light_->GetRange(), M_EPSILON), 0.0f, 0.0f, 0.0f);
-            graphics->SetShaderParameter(PSP_LIGHTATTEN, light_Atten);
+            Vector4 lightAtten(1.0f / Max(light_->GetRange(), M_EPSILON), 0.0f, 0.0f, 0.0f);
+            graphics->SetShaderParameter(PSP_LIGHTATTEN, lightAtten);
         }
         
         if (graphics->NeedParameterUpdate(PSP_LIGHTCOLOR, light_))
@@ -233,7 +232,7 @@ void Batch::Prepare(Graphics* graphics, const HashMap<StringHash, Vector4>& shad
         {
             Matrix3x4 viewPos(camera_->GetWorldPosition(), Quaternion::IDENTITY, Vector3::UNITY);
             Matrix3x4 spotView(light_->GetWorldPosition(), light_->GetWorldRotation(), 1.0f);
-            Matrix4 spotProj(Matrix4::IDENTITY);
+            Matrix4 spotProj(Matrix4::ZERO);
             Matrix4 texAdjust(Matrix4::IDENTITY);
             
             // Make the projected light slightly smaller than the shadow map to prevent light spill
@@ -422,7 +421,7 @@ void BatchGroup::Draw(Graphics* graphics, VertexBuffer* instanceBuffer, const Ha
         Vector<SharedPtr<ShaderVariation> >& vertexShaders = pass_->GetVertexShaders();
         Vector<SharedPtr<ShaderVariation> >& pixelShaders = pass_->GetPixelShaders();
         PassType type = pass_->GetType();
-        if (type != PASS_LITBASE && type != PASS_LIGHT)
+        if (type != PASS_LIGHT)
             batch.vertexShader_ = vertexShaders[vertexShaderIndex_ + GEOM_INSTANCED];
         else
             batch.vertexShader_ = vertexShaders[vertexShaderIndex_ + GEOM_INSTANCED * MAX_LIGHT_VS_VARIATIONS];

+ 0 - 2
Engine/Graphics/BillboardSet.cpp

@@ -424,8 +424,6 @@ void BillboardSet::UpdateVertexBuffer(const FrameInfo& frame)
     for (unsigned i = 0; i < enabledBillboards; ++i)
     {
         Billboard& billboard = *sortedBillboards_[i];
-        if (!billboard.enabled_)
-            continue;
         
         Vector3 position(billboardTransform * billboard.position_);
         Vector2 size(billboard.size_.x_ * billboardScale.x_, billboard.size_.y_ * billboardScale.y_);

+ 1 - 8
Engine/Graphics/Drawable.cpp

@@ -55,7 +55,6 @@ Drawable::Drawable(Context* context) :
     lodDistance_(0.0f),
     sortValue_(0.0f),
     viewFrameNumber_(0),
-    basePassFlags_(0),
     viewCamera_(0),
     worldBoundingBoxDirty_(true),
     lodLevelsDirty_(true)
@@ -185,17 +184,11 @@ void Drawable::SetSortValue(float value)
     sortValue_ = value;
 }
 
-void Drawable::ClearBasePass()
+void Drawable::ClearLights()
 {
-    basePassFlags_ = 0;
     lights_.Clear();
 }
 
-void Drawable::SetBasePass(unsigned batchIndex)
-{
-    basePassFlags_ |= (1 << batchIndex);
-}
-
 void Drawable::AddLight(Light* light)
 {
     lights_.Push(light);

+ 2 - 8
Engine/Graphics/Drawable.h

@@ -142,10 +142,8 @@ public:
     void MarkInView(const FrameInfo& frame);
     /// Mark in a shadow camera view this frame. If an actual view is already set, does not override it. Called by View.
     void MarkInShadowView(const FrameInfo& frame);
-    /// Clear base pass flags. Also resets light vector.
-    void ClearBasePass();
-    /// %Set base pass flag for a batch.
-    void SetBasePass(unsigned batchIndex);
+    /// Clear light vector.
+    void ClearLights();
     /// Add a light, for drawables that limit the maximum light count.
     void AddLight(Light* light);
     /// Sort and limit lights to maximum allowed.
@@ -160,8 +158,6 @@ public:
     bool IsInView(unsigned frameNumber) const;
     /// Return whether is visible in a specific view this frame.
     bool IsInView(const FrameInfo& frame) const;
-    /// Return whether has a base pass.
-    bool HasBasePass(unsigned batchIndex) const { return (basePassFlags_ & (1 << batchIndex)) != 0; }
     /// Return lights.
     const PODVector<Light*>& GetLights() const { return lights_; }
     
@@ -211,8 +207,6 @@ protected:
     float sortValue_;
     /// Last visible frame number.
     unsigned viewFrameNumber_;
-    /// Base pass flags per batch index.
-    unsigned basePassFlags_;
     /// Last camera rendered from. Not safe to dereference.
     Camera* viewCamera_;
     /// Lights affecting this drawable, when light count is limited.

+ 0 - 1
Engine/Graphics/GraphicsDefs.h

@@ -174,7 +174,6 @@ enum PassType
 {
     PASS_GBUFFER,
     PASS_BASE,
-    PASS_LITBASE,
     PASS_LIGHT,
     PASS_EXTRA,
     PASS_SHADOW,

+ 5 - 3
Engine/Graphics/Light.cpp

@@ -286,11 +286,12 @@ void Light::SetShapeTexture(Texture* texture)
 
 void Light::CopyFrom(Light* original)
 {
-    node_->SetTransform(original->GetWorldPosition(), original->GetWorldRotation(), Vector3::UNITY);
+    node_->SetTransform(original->GetWorldPosition(), original->GetWorldRotation(), original->GetWorldScale());
     castShadows_ = original->castShadows_;
     drawDistance_ = original->drawDistance_;
     shadowDistance_ = original->shadowDistance_;
     viewMask_ = original->viewMask_;
+    lightMask_ = original->lightMask_;
     distance_ = original->distance_;
     lightType_ = original->lightType_;
     range_ = original->range_;
@@ -330,7 +331,7 @@ float Light::GetVolumeExtent() const
     case LIGHT_SPOT:
         {
             float safeRange = range_ * 1.001f;
-            float yScale = tan(fov_ * M_DEGTORAD * 0.5f) * safeRange;
+            float yScale = tanf(fov_ * M_DEGTORAD * 0.5f) * safeRange;
             float xScale = aspectRatio_ * yScale;
             return sqrtf(xScale * xScale + yScale * yScale + safeRange * safeRange);
         }
@@ -366,6 +367,7 @@ Matrix3x4 Light::GetDirLightTransform(Camera& camera, bool getNearQuad)
         farVector.z_ *= (distance / farClip);
     
     // Set an epsilon from clip planes due to possible inaccuracy
+    /// \todo Rather set an identity projection matrix
     farVector.z_ = Clamp(farVector.z_, (1.0f + M_LARGE_EPSILON) * nearClip, (1.0f - M_LARGE_EPSILON) * farClip);
     
     return  Matrix3x4(Vector3(0.0f, 0.0f, farVector.z_), Quaternion::IDENTITY, Vector3(farVector.x_, farVector.y_, 1.0f));
@@ -387,7 +389,7 @@ const Matrix3x4& Light::GetVolumeTransform(Camera& camera)
         
     case LIGHT_SPOT:
         {
-            float yScale = tan(fov_ * M_DEGTORAD * 0.5f) * range_;
+            float yScale = tanf(fov_ * M_DEGTORAD * 0.5f) * range_;
             float xScale = aspectRatio_ * yScale;
             volumeTransform_ = Matrix3x4(transform.Translation(), transform.Rotation(), Vector3(xScale, yScale, range_));
         }

+ 4 - 10
Engine/Graphics/Renderer.cpp

@@ -883,7 +883,7 @@ void Renderer::SetBatchShaders(Batch& batch, Technique* technique, Pass* pass, b
     {
         //  Check whether is a forward lit pass. If not, there is only one pixel shader
         PassType type = pass->GetType();
-        if (type != PASS_LITBASE && type != PASS_LIGHT)
+        if (type != PASS_LIGHT)
         {
             unsigned vsi = batch.geometryType_;
             batch.vertexShader_ = vertexShaders[vsi];
@@ -1043,7 +1043,6 @@ void Renderer::LoadMaterialShaders(Technique* technique)
     if (mode == RENDER_FORWARD)
     {
         LoadPassShaders(technique, PASS_BASE);
-        LoadPassShaders(technique, PASS_LITBASE);
         LoadPassShaders(technique, PASS_LIGHT);
     }
     else
@@ -1054,7 +1053,6 @@ void Renderer::LoadMaterialShaders(Technique* technique)
         {
             LoadPassShaders(technique, PASS_BASE);
             // If shadow maps are not reused, transparencies can be rendered shadowed
-            LoadPassShaders(technique, PASS_LITBASE, !reuseShadowMaps_);
             LoadPassShaders(technique, PASS_LIGHT, !reuseShadowMaps_);
         }
     }
@@ -1076,7 +1074,7 @@ void Renderer::LoadPassShaders(Technique* technique, PassType pass, bool allowSh
         pixelShaderName += "_";
     
     // If ambient pass is transparent, and shadow maps are reused, do not load shadow variations
-    if (reuseShadowMaps_ && (pass == PASS_LIGHT || pass == PASS_LITBASE))
+    if (reuseShadowMaps_ && pass == PASS_LIGHT)
     {
         if (!technique->HasPass(PASS_BASE) || technique->GetPass(PASS_BASE)->GetBlendMode() != BLEND_REPLACE)
             allowShadows = false;
@@ -1101,14 +1099,10 @@ void Renderer::LoadPassShaders(Technique* technique, PassType pass, bool allowSh
         pixelShaders[0] = GetPixelShader(pixelShaderName);
         break;
         
-    case PASS_LITBASE:
     case PASS_LIGHT:
         {
-            // In first light pass, load only directional light shaders
-            unsigned numPS = i->first_ == PASS_LIGHT ? MAX_LIGHT_PS_VARIATIONS : LPS_SPOT;
-            
             vertexShaders.Resize(MAX_GEOMETRYTYPES * MAX_LIGHT_VS_VARIATIONS);
-            pixelShaders.Resize(numPS);
+            pixelShaders.Resize(MAX_LIGHT_PS_VARIATIONS);
             
             for (unsigned j = 0; j < MAX_GEOMETRYTYPES * MAX_LIGHT_VS_VARIATIONS; ++j)
             {
@@ -1119,7 +1113,7 @@ void Renderer::LoadPassShaders(Technique* technique, PassType pass, bool allowSh
                 else
                     vertexShaders[j].Reset();
             }
-            for (unsigned j = 0; j < numPS; ++j)
+            for (unsigned j = 0; j < MAX_LIGHT_PS_VARIATIONS; ++j)
             {
                 unsigned variation = j % LPS_SPOT;
                 if (variation == LPS_SHADOW || variation == LPS_SHADOWSPEC)

+ 0 - 1
Engine/Graphics/Technique.cpp

@@ -35,7 +35,6 @@ static const String passNames[] =
 {
     "gbuffer",
     "base",
-    "litbase",
     "light",
     "extra",
     "shadow",

+ 86 - 104
Engine/Graphics/View.cpp

@@ -347,7 +347,7 @@ void View::GetDrawables()
         unsigned flags = drawable->GetDrawableFlags();
         if (flags & DRAWABLE_GEOMETRY)
         {
-            drawable->ClearBasePass();
+            drawable->ClearLights();
             drawable->MarkInView(frame_);
             drawable->UpdateGeometry(frame_);
             
@@ -391,6 +391,87 @@ void View::GetBatches()
     HashSet<Drawable*> maxLightsDrawables;
     Map<Light*, unsigned> lightQueueIndex;
     
+    // Go through geometries for base pass batches
+    {
+        PROFILE(GetBaseBatches);
+        for (unsigned i = 0; i < geometries_.Size(); ++i)
+        {
+            Drawable* drawable = geometries_[i];
+            unsigned numBatches = drawable->GetNumBatches();
+            
+            for (unsigned j = 0; j < numBatches; ++j)
+            {
+                Batch baseBatch;
+                drawable->GetBatch(frame_, j, baseBatch);
+                
+                Technique* tech = GetTechnique(drawable, baseBatch.material_);
+                if (!baseBatch.geometry_ || !tech)
+                    continue;
+                
+                // Check here if the material technique refers to a render target texture with camera(s) attached
+                // Only check this for the main view (null rendertarget)
+                if (!renderTarget_ && baseBatch.material_ && baseBatch.material_->GetAuxViewFrameNumber() != frame_.frameNumber_)
+                    CheckMaterialForAuxView(baseBatch.material_);
+                
+                // Fill the rest of the batch
+                baseBatch.camera_ = camera_;
+                baseBatch.distance_ = drawable->GetDistance();
+                
+                Pass* pass = 0;
+                // In deferred mode, check for a G-buffer batch first
+                if (mode_ != RENDER_FORWARD)
+                {
+                    pass = tech->GetPass(PASS_GBUFFER);
+                    if (pass)
+                    {
+                        renderer_->SetBatchShaders(baseBatch, tech, pass);
+                        baseBatch.hasPriority_ = !pass->GetAlphaTest() && !pass->GetAlphaMask();
+                        gBufferQueue_.AddBatch(baseBatch);
+                        
+                        // Check also for an additional pass (possibly for emissive)
+                        pass = tech->GetPass(PASS_EXTRA);
+                        if (pass)
+                        {
+                            renderer_->SetBatchShaders(baseBatch, tech, pass);
+                            baseQueue_.AddBatch(baseBatch);
+                        }
+                        
+                        continue;
+                    }
+                }
+                
+                // Then check for forward rendering base pass
+                pass = tech->GetPass(PASS_BASE);
+                if (pass)
+                {
+                    renderer_->SetBatchShaders(baseBatch, tech, pass);
+                    if (pass->GetBlendMode() == BLEND_REPLACE)
+                    {
+                        baseBatch.hasPriority_ = !pass->GetAlphaTest() && !pass->GetAlphaMask();
+                        baseQueue_.AddBatch(baseBatch);
+                    }
+                    else
+                    {
+                        baseBatch.hasPriority_ = true;
+                        transparentQueue_.AddBatch(baseBatch, true);
+                    }
+                    continue;
+                }
+                else
+                {
+                    // If no base pass, finally check for extra / custom pass
+                    pass = tech->GetPass(PASS_EXTRA);
+                    if (pass)
+                    {
+                        baseBatch.hasPriority_ = false;
+                        renderer_->SetBatchShaders(baseBatch, tech, pass);
+                        extraQueue_.AddBatch(baseBatch);
+                    }
+                }
+            }
+        }
+    }
+    
     // Go through lights
     {
         PROFILE_MULTIPLE(GetLightBatches, lights_.Size());
@@ -539,91 +620,6 @@ void View::GetBatches()
         }
     }
     
-    // Go through geometries for base pass batches
-    {
-        PROFILE(GetBaseBatches);
-        for (unsigned i = 0; i < geometries_.Size(); ++i)
-        {
-            Drawable* drawable = geometries_[i];
-            unsigned numBatches = drawable->GetNumBatches();
-            
-            for (unsigned j = 0; j < numBatches; ++j)
-            {
-                Batch baseBatch;
-                drawable->GetBatch(frame_, j, baseBatch);
-                
-                Technique* tech = GetTechnique(drawable, baseBatch.material_);
-                if (!baseBatch.geometry_ || !tech)
-                    continue;
-                
-                // Check here if the material technique refers to a render target texture with camera(s) attached
-                // Only check this for the main view (null rendertarget)
-                if (!renderTarget_ && baseBatch.material_ && baseBatch.material_->GetAuxViewFrameNumber() != frame_.frameNumber_)
-                    CheckMaterialForAuxView(baseBatch.material_);
-                
-                // If object already has a lit base pass, can skip the unlit base pass
-                if (drawable->HasBasePass(j))
-                    continue;
-                
-                // Fill the rest of the batch
-                baseBatch.camera_ = camera_;
-                baseBatch.distance_ = drawable->GetDistance();
-                
-                Pass* pass = 0;
-                // In deferred mode, check for a G-buffer batch first
-                if (mode_ != RENDER_FORWARD)
-                {
-                    pass = tech->GetPass(PASS_GBUFFER);
-                    if (pass)
-                    {
-                        renderer_->SetBatchShaders(baseBatch, tech, pass);
-                        baseBatch.hasPriority_ = !pass->GetAlphaTest() && !pass->GetAlphaMask();
-                        gBufferQueue_.AddBatch(baseBatch);
-                        
-                        // Check also for an additional pass (possibly for emissive)
-                        pass = tech->GetPass(PASS_EXTRA);
-                        if (pass)
-                        {
-                            renderer_->SetBatchShaders(baseBatch, tech, pass);
-                            baseQueue_.AddBatch(baseBatch);
-                        }
-                        
-                        continue;
-                    }
-                }
-                
-                // Then check for forward rendering base pass
-                pass = tech->GetPass(PASS_BASE);
-                if (pass)
-                {
-                    renderer_->SetBatchShaders(baseBatch, tech, pass);
-                    if (pass->GetBlendMode() == BLEND_REPLACE)
-                    {
-                        baseBatch.hasPriority_ = !pass->GetAlphaTest() && !pass->GetAlphaMask();
-                        baseQueue_.AddBatch(baseBatch);
-                    }
-                    else
-                    {
-                        baseBatch.hasPriority_ = true;
-                        transparentQueue_.AddBatch(baseBatch, true);
-                    }
-                    continue;
-                }
-                else
-                {
-                    // If no base pass, finally check for extra / custom pass
-                    pass = tech->GetPass(PASS_EXTRA);
-                    if (pass)
-                    {
-                        baseBatch.hasPriority_ = false;
-                        renderer_->SetBatchShaders(baseBatch, tech, pass);
-                        extraQueue_.AddBatch(baseBatch);
-                    }
-                }
-            }
-        }
-    }
-    
     // All batches have been collected. Sort them now
     SortBatches();
 }
@@ -651,22 +647,8 @@ void View::GetLitBatches(Drawable* drawable, Light* light, Light* splitLight, Li
         
         Pass* pass = 0;
         bool priority = false;
-        
-        // For directional light, check for lit base pass
-        if (splitLight->GetLightType() == LIGHT_DIRECTIONAL)
-        {
-            if (!drawable->HasBasePass(i))
-            {
-                pass = tech->GetPass(PASS_LITBASE);
-                if (pass)
-                {
-                    priority = true;
-                    drawable->SetBasePass(i);
-                }
-            }
-        }
-        
-        // If no lit base pass, get ordinary light pass
+         
+        // Get lit pass
         if (!pass)
             pass = tech->GetPass(PASS_LIGHT);
         // Skip if material does not receive light at all
@@ -1562,9 +1544,9 @@ void View::SetupShadowCamera(Light* light, bool shadowOcclusion)
                 if (lightPixels < SHADOW_MIN_PIXELS)
                     lightPixels = SHADOW_MIN_PIXELS;
                 
-                float zoolevel_ = Min(lightPixels / (float)light->GetShadowMap()->GetHeight(), 1.0f);
+                float zoomLevel = Min(lightPixels / (float)light->GetShadowMap()->GetHeight(), 1.0f);
                 
-                shadowCamera->SetZoom(zoolevel_);
+                shadowCamera->SetZoom(zoomLevel);
             }
         }
         break;

+ 7 - 8
Engine/Math/Quaternion.cpp

@@ -93,17 +93,16 @@ Quaternion::Quaternion(const Vector3& start, const Vector3& end)
 
 Quaternion::Quaternion(const Matrix3& matrix)
 {
-    float t = matrix.m00_ + matrix.m11_ + matrix.m22_ + 1.0f;
+    float t = matrix.m00_ + matrix.m11_ + matrix.m22_;
     
-    if (t > 0.001f)
+    if (t > 0.0f)
     {
-        float s = sqrtf(t) * 2.0f;
-        float invS = 1.0f / s;
+        float s = 0.5f / sqrtf(1.0f + t);
         
-        x_ = (matrix.m21_ - matrix.m12_) * invS;
-        y_ = (matrix.m02_ - matrix.m20_) * invS;
-        z_ = (matrix.m10_ - matrix.m01_) * invS;
-        w_ = 0.25f * s;
+        x_ = (matrix.m21_ - matrix.m12_) * s;
+        y_ = (matrix.m02_ - matrix.m20_) * s;
+        z_ = (matrix.m10_ - matrix.m01_) * s;
+        w_ = 0.25f / s;
     }
     else
     {

+ 1 - 12
SourceAssets/GLSLShaders/Forward.xml

@@ -17,18 +17,12 @@
         <option name="Diff" define="DIFFMAP" />
         <option name="Normal" define="NORMALMAP" />
         <option name="SpecMap" define="SPECMAP" include="Spec" />
-        <option name="VCol" define="VERTEXCOLOR">
-            <exclude name="Normal" />
-            <exclude name="Shadow" />
-            <exclude name="Spec" />
-            <exclude name="SpecMap" />
-        </option>
+        <option name="VCol" define="VERTEXCOLOR" />
         <option name="Volume" define="VOLUMETRIC">
             <exclude name="Additive" />
             <exclude name="Ambient" />
             <exclude name="Normal" />
             <exclude name="Shadow" />
-            <exclude name="Spec" />
             <exclude name="SpecMap" />
             <exclude name="Unlit" />
         </option>
@@ -47,11 +41,6 @@
             <exclude name="Spec" />
             <exclude name="SpecMap" />
         </variation>
-        <variation name="AmbientDir">
-            <define name="AMBIENT" />
-            <define name="DIRLIGHT" />
-            <define name="LIGHT" />
-        </variation>
         <variation name="Dir">
             <define name="DIRLIGHT" />
             <define name="LIGHT" />

+ 1 - 12
SourceAssets/HLSLShaders/Forward.xml

@@ -18,18 +18,12 @@
         <option name="Diff" define="DIFFMAP" />
         <option name="Normal" define="NORMALMAP" />
         <option name="SpecMap" define="SPECMAP" include="Spec" />
-        <option name="VCol" define="VERTEXCOLOR">
-            <exclude name="Normal" />
-            <exclude name="Shadow" />
-            <exclude name="Spec" />
-            <exclude name="SpecMap" />
-        </option>
+        <option name="VCol" define="VERTEXCOLOR" />
         <option name="Volume" define="VOLUMETRIC">
             <exclude name="Additive" />
             <exclude name="Ambient" />
             <exclude name="Normal" />
             <exclude name="Shadow" />
-            <exclude name="Spec" />
             <exclude name="SpecMap" />
             <exclude name="Unlit" />
         </option>
@@ -48,11 +42,6 @@
             <exclude name="Spec" />
             <exclude name="SpecMap" />
         </variation>
-        <variation name="AmbientDir">
-            <define name="AMBIENT" />
-            <define name="DIRLIGHT" />
-            <define name="LIGHT" />
-        </variation>
         <variation name="Dir">
             <define name="DIRLIGHT" />
             <define name="LIGHT" />