=== added file 'data/shaders/pulsar-light.vert'
--- data/shaders/pulsar-light.vert	1970-01-01 00:00:00 +0000
+++ data/shaders/pulsar-light.vert	2011-08-17 09:21:24 +0000
@@ -0,0 +1,31 @@
+attribute vec3 position;
+attribute vec3 normal;
+attribute vec4 vtxcolor;
+attribute vec2 texcoord;
+
+uniform mat4 ModelViewProjectionMatrix;
+uniform mat4 NormalMatrix;
+
+varying vec4 Color;
+varying vec2 TextureCoord;
+
+void main(void)
+{
+    // Transform the normal to eye coordinates
+    vec3 N = normalize(vec3(NormalMatrix * vec4(normal, 1.0)));
+
+    // The LightSourcePosition is actually its direction for directional light
+    vec3 L = normalize(LightSourcePosition.xyz);
+
+    // Multiply the diffuse value by the vertex color
+    // to get the actual color that we will use to draw this vertex with
+    float diffuse = max(abs(dot(N, L)), 0.0);
+    Color = diffuse * vtxcolor;
+
+    // Set the texture coordinates as a varying
+    TextureCoord = texcoord;
+
+    // Transform the position to clip coordinates
+    gl_Position = ModelViewProjectionMatrix * vec4(position, 1.0);
+}
+

=== added file 'data/shaders/pulsar.vert'
--- data/shaders/pulsar.vert	1970-01-01 00:00:00 +0000
+++ data/shaders/pulsar.vert	2011-08-17 09:21:24 +0000
@@ -0,0 +1,19 @@
+attribute vec3 position;
+attribute vec4 vtxcolor;
+attribute vec2 texcoord;
+attribute vec3 normal;
+
+uniform mat4 ModelViewProjectionMatrix;
+
+varying vec4 Color;
+varying vec2 TextureCoord;
+
+void main(void)
+{
+    Color = vtxcolor;
+    TextureCoord = texcoord;
+
+    // Transform the position to clip coordinates
+    gl_Position = ModelViewProjectionMatrix * vec4(position, 1.0);
+}
+

=== modified file 'src/android.cpp'
--- src/android.cpp	2011-08-16 13:10:09 +0000
+++ src/android.cpp	2011-08-18 15:03:40 +0000
@@ -43,6 +43,7 @@
     "bump:bump-render=normals",
     "effect2d:kernel=0,1,0;1,-4,1;0,1,0;",
     "effect2d:kernel=1,1,1,1,1;1,1,1,1,1;1,1,1,1,1;",
+    "pulsar:quads=5:texture=false:light=false",
     "conditionals:vertex-steps=0:fragment-steps=0",
     "conditionals:vertex-steps=0:fragment-steps=5",
     "conditionals:vertex-steps=5:fragment-steps=0",
@@ -83,6 +84,7 @@
     Benchmark::register_scene(*new SceneLoop(*g_canvas));
     Benchmark::register_scene(*new SceneBump(*g_canvas));
     Benchmark::register_scene(*new SceneEffect2D(*g_canvas));
+    Benchmark::register_scene(*new ScenePulsar(*g_canvas));
 
     add_default_benchmarks(g_benchmarks);
 

=== modified file 'src/main.cpp'
--- src/main.cpp	2011-08-09 11:13:43 +0000
+++ src/main.cpp	2011-08-18 15:03:40 +0000
@@ -52,6 +52,7 @@
     "bump:bump-render=normals",
     "effect2d:kernel=0,1,0;1,-4,1;0,1,0;",
     "effect2d:kernel=1,1,1,1,1;1,1,1,1,1;1,1,1,1,1;",
+    "pulsar:quads=5:texture=false:light=false",
     "conditionals:vertex-steps=0:fragment-steps=0",
     "conditionals:vertex-steps=0:fragment-steps=5",
     "conditionals:vertex-steps=5:fragment-steps=0",
@@ -229,6 +230,7 @@
     Benchmark::register_scene(*new SceneLoop(canvas));
     Benchmark::register_scene(*new SceneBump(canvas));
     Benchmark::register_scene(*new SceneEffect2D(canvas));
+    Benchmark::register_scene(*new ScenePulsar(canvas));
 
     if (Options::list_scenes) {
         list_scenes();

=== added file 'src/scene-pulsar.cpp'
--- src/scene-pulsar.cpp	1970-01-01 00:00:00 +0000
+++ src/scene-pulsar.cpp	2011-08-18 13:43:31 +0000
@@ -0,0 +1,294 @@
+/*
+ * Copyright © 2008 Ben Smith
+ * Copyright © 2010-2011 Linaro Limited
+ *
+ * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
+ *
+ * glmark2 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * glmark2.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Ben Smith (original glmark benchmark)
+ *  Alexandros Frantzis (glmark2)
+ *  Marc Ordinas i Llopis, Collabora Ltd. (pulsar scene)
+ */
+#include "scene.h"
+#include "mat.h"
+#include "stack.h"
+#include "vec.h"
+#include "log.h"
+#include "program.h"
+#include "shader-source.h"
+
+#include <cmath>
+
+ScenePulsar::ScenePulsar(Canvas &pCanvas) :
+    Scene(pCanvas, "pulsar"),
+    mTexture(0)
+{
+    mOptions["quads"] = Scene::Option("quads", "5", "Number of quads to render");
+    mOptions["texture"] = Scene::Option("texture", "false", "Enable texturing");
+    mOptions["light"] = Scene::Option("light", "false", "Enable lighting");
+    mOptions["random"] = Scene::Option("random", "false", "Enable random rotation speeds");
+}
+
+ScenePulsar::~ScenePulsar()
+{
+}
+
+int ScenePulsar::load()
+{
+    mScale = LibMatrix::vec3(1.0, 1.0, 1.0);
+
+    mRunning = false;
+
+    return 1;
+}
+
+void ScenePulsar::unload()
+{
+}
+
+void ScenePulsar::setup()
+{
+    Scene::setup();
+
+    // Disable back-face culling
+    glDisable(GL_CULL_FACE);
+    // Enable alpha blending
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+    // Create a rotation for each quad.
+    std::stringstream ss;
+    ss << mOptions["quads"].value;
+    ss >> mNumQuads;
+
+    srand((unsigned)time(0));
+    for (int i = 0; i < mNumQuads; i++) {
+        mRotations.push_back(LibMatrix::vec3());
+        if (mOptions["random"].value == "true") {
+            mRotationSpeeds.push_back(LibMatrix::vec3(((float)rand() / (float)RAND_MAX) * 5.0,
+                                                      ((float)rand() / (float)RAND_MAX) * 5.0,
+                                                      0.0));
+        }
+        else {
+            float integral;
+            float x_rot = std::modf((i + 1) * M_PI, &integral);
+            float y_rot = std::modf((i + 1) * M_E, &integral);
+            mRotationSpeeds.push_back(LibMatrix::vec3(x_rot * 5.0,
+                                                      y_rot * 5.0,
+                                                      0.0));
+        }
+    }
+
+    // Load shaders
+    std::string vtx_shader_filename;
+    std::string frg_shader_filename;
+    static const LibMatrix::vec4 lightPosition(-20.0f, 20.0f,-20.0f, 1.0f);
+    if (mOptions["light"].value == "true") {
+        vtx_shader_filename = GLMARK_DATA_PATH"/shaders/pulsar-light.vert";
+    } else {
+        vtx_shader_filename = GLMARK_DATA_PATH"/shaders/pulsar.vert";
+    }
+
+    if (mOptions["texture"].value == "true") {
+        frg_shader_filename = GLMARK_DATA_PATH"/shaders/light-basic-tex.frag";
+        Texture::load(GLMARK_DATA_PATH"/textures/crate-base.png", &mTexture,
+                      GL_NEAREST, GL_NEAREST, 0);
+
+    } else {
+        frg_shader_filename = GLMARK_DATA_PATH"/shaders/light-basic.frag";
+    }
+
+    ShaderSource vtx_source(vtx_shader_filename);
+    ShaderSource frg_source(frg_shader_filename);
+    if (mOptions["light"].value == "true") {
+        // Load the light position constant
+        vtx_source.add_const("LightSourcePosition", lightPosition);
+    }
+
+    if (!Scene::load_shaders_from_strings(mProgram, vtx_source.str(),
+                                          frg_source.str()))
+    {
+        return;
+    }
+
+    create_and_setup_mesh();
+
+    mProgram.start();
+
+    mCurrentFrame = 0;
+
+    mRunning = true;
+    mStartTime = Scene::get_timestamp_us() / 1000000.0;
+    mLastUpdateTime = mStartTime;
+}
+
+void ScenePulsar::teardown()
+{
+    mProgram.stop();
+    mProgram.release();
+
+    if (mOptions["texture"].value == "true") {
+        glDeleteTextures(1, &mTexture);
+        mTexture = 0;
+    }
+
+    // Re-enable back-face culling
+    glEnable(GL_CULL_FACE);
+    // Disable alpha blending
+    glDisable(GL_BLEND);
+
+    mPlaneMesh.reset();
+
+    Scene::teardown();
+}
+
+void ScenePulsar::update()
+{
+    double current_time = Scene::get_timestamp_us() / 1000000.0;
+    double dt = current_time - mLastUpdateTime;
+    double elapsed_time = current_time - mStartTime;
+
+    mLastUpdateTime = current_time;
+
+    if (elapsed_time >= mDuration) {
+        mAverageFPS = mCurrentFrame / elapsed_time;
+        mRunning = false;
+    }
+
+    for (int i = 0; i < mNumQuads; i++) {
+        mRotations[i] += mRotationSpeeds[i] * (dt * 60);
+    }
+
+    mScale = LibMatrix::vec3(cos(elapsed_time / 3.60) * 10.0, sin(elapsed_time / 3.60) * 10.0, 1.0);
+
+    mCurrentFrame++;
+}
+
+void ScenePulsar::draw()
+{
+    if (mOptions["texture"].value == "true") {
+        glActiveTexture(GL_TEXTURE0);
+        glBindTexture(GL_TEXTURE_2D, mTexture);
+    }
+
+    for (int i = 0; i < mNumQuads; i++) {
+        // Load the ModelViewProjectionMatrix uniform in the shader
+        LibMatrix::Stack4 model_view;
+        LibMatrix::mat4 model_view_proj(mCanvas.projection());
+        model_view.scale(mScale.x(), mScale.y(), mScale.z());
+        model_view.translate(0.0f, 0.0f, -10.0f);
+        model_view.rotate(mRotations[i].x(), 1.0f, 0.0f, 0.0f);
+        model_view.rotate(mRotations[i].y(), 0.0f, 1.0f, 0.0f);
+        model_view.rotate(mRotations[i].z(), 0.0f, 0.0f, 1.0f);
+        model_view_proj *= model_view.getCurrent();
+        mProgram.loadUniformMatrix(model_view_proj, "ModelViewProjectionMatrix");
+
+        if (mOptions["light"].value == "true") {
+            // Load the NormalMatrix uniform in the shader. The NormalMatrix is the
+            // inverse transpose of the model view matrix.
+            LibMatrix::mat4 normal_matrix(model_view.getCurrent());
+            normal_matrix.inverse().transpose();
+            mProgram.loadUniformMatrix(normal_matrix, "NormalMatrix");
+        }
+
+        mPlaneMesh.render_vbo();
+    }
+}
+
+Scene::ValidationResult
+ScenePulsar::validate()
+{
+    return ValidationUnknown;
+}
+
+void ScenePulsar::create_and_setup_mesh()
+{
+    bool texture = mOptions["texture"].value == "true";
+    bool light = mOptions["light"].value == "true";
+
+    struct PlaneMeshVertex {
+        LibMatrix::vec3 position;
+        LibMatrix::vec4 color;
+        LibMatrix::vec2 texcoord;
+        LibMatrix::vec3 normal;
+    };
+
+    PlaneMeshVertex plane_vertices[] = {
+        {
+          LibMatrix::vec3(-1.0, -1.0, 0.0),
+          LibMatrix::vec4(1.0, 0.0, 0.0, 0.4),
+          LibMatrix::vec2(0.0, 0.0),
+          LibMatrix::vec3(0.0, 0.0, 1.0)
+        },
+        {
+          LibMatrix::vec3(-1.0, 1.0, 0.0),
+          LibMatrix::vec4(0.0, 1.0, 0.0, 0.4),
+          LibMatrix::vec2(0.0, 1.0),
+          LibMatrix::vec3(0.0, 0.0, 1.0)
+        },
+        {
+          LibMatrix::vec3(1.0, 1.0, 0.0),
+          LibMatrix::vec4(0.0, 0.0, 1.0, 0.4),
+          LibMatrix::vec2(1.0, 1.0),
+          LibMatrix::vec3(0.0, 0.0, 1.0)
+        },
+        {
+          LibMatrix::vec3(1.0, -1.0, 0.0),
+          LibMatrix::vec4(1.0, 1.0, 1.0, 1.0),
+          LibMatrix::vec2(1.0, 0.0),
+          LibMatrix::vec3(0.0, 0.0, 1.0)
+        }
+    };
+
+    unsigned int vertex_index[] = {0, 1, 2, 0, 2, 3};
+
+    // Set vertex format
+    std::vector<int> vertex_format;
+    vertex_format.push_back(3);     // Position
+    vertex_format.push_back(4);     // Color
+    if (texture)
+        vertex_format.push_back(2); // Texcoord
+    if (light)
+        vertex_format.push_back(3); // Normal
+
+    mPlaneMesh.set_vertex_format(vertex_format);
+
+    // Build the plane mesh
+    for (size_t i = 0; i < sizeof(vertex_index) / sizeof(*vertex_index); i++) {
+        PlaneMeshVertex& vertex = plane_vertices[vertex_index[i]];
+
+        mPlaneMesh.next_vertex();
+        mPlaneMesh.set_attrib(0, vertex.position);
+        mPlaneMesh.set_attrib(1, vertex.color);
+        if (texture)
+            mPlaneMesh.set_attrib(2, vertex.texcoord);
+        if (light)
+            mPlaneMesh.set_attrib(2 + ((int)texture), vertex.normal);
+    }
+
+    mPlaneMesh.build_vbo();
+
+    // Set attribute locations
+    std::vector<GLint> attrib_locations;
+    attrib_locations.push_back(mProgram.getAttribIndex("position"));
+    attrib_locations.push_back(mProgram.getAttribIndex("vtxcolor"));
+    if (texture)
+        attrib_locations.push_back(mProgram.getAttribIndex("texcoord"));
+    if (light)
+        attrib_locations.push_back(mProgram.getAttribIndex("normal"));
+    mPlaneMesh.set_attrib_locations(attrib_locations);
+}
+

=== modified file 'src/scene.h'
--- src/scene.h	2011-08-16 07:37:52 +0000
+++ src/scene.h	2011-08-18 13:20:04 +0000
@@ -20,6 +20,7 @@
  * Authors:
  *  Ben Smith (original glmark benchmark)
  *  Alexandros Frantzis (glmark2)
+ *  Marc Ordinas i Llopis, Collabora Ltd. (pulsar scene)
  */
 #ifndef GLMARK2_SCENE_H_
 #define GLMARK2_SCENE_H_
@@ -300,4 +301,34 @@
     Mesh mesh_;
     GLuint texture_;
 };
+
+class ScenePulsar : public Scene
+{
+public:
+    ScenePulsar(Canvas &pCanvas);
+    int load();
+    void unload();
+    void setup();
+    void teardown();
+    void update();
+    void draw();
+    ValidationResult validate();
+
+    ~ScenePulsar();
+
+protected:
+    int mNumQuads;
+    Program mProgram;
+
+    Mesh mPlaneMesh;
+    LibMatrix::vec3 mScale;
+    std::vector<LibMatrix::vec3> mRotations;
+    std::vector<LibMatrix::vec3> mRotationSpeeds;
+
+    GLuint mTexture;
+
+private:
+    void create_and_setup_mesh();
+};
+
 #endif

