1 /*
2  * Copyright © 2010-2011 Linaro Limited
3  *
4  * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
5  *
6  * glmark2 is free software: you can redistribute it and/or modify it under the
7  * terms of the GNU General Public License as published by the Free Software
8  * Foundation, either version 3 of the License, or (at your option) any later
9  * version.
10  *
11  * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
12  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
14  * details.
15  *
16  * You should have received a copy of the GNU General Public License along with
17  * glmark2.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  * Authors:
20  *  Alexandros Frantzis (glmark2)
21  *  Jesse Barker (glmark2)
22  */
23 #include "scene.h"
24 #include "log.h"
25 #include "mat.h"
26 #include "options.h"
27 #include "stack.h"
28 #include "shader-source.h"
29 #include "model.h"
30 #include "texture.h"
31 #include "util.h"
32 #include <cmath>
33 
SceneBump(Canvas & pCanvas)34 SceneBump::SceneBump(Canvas &pCanvas) :
35     Scene(pCanvas, "bump"),
36     texture_(0), rotation_(0.0f), rotationSpeed_(0.0f)
37 {
38     options_["bump-render"] = Scene::Option("bump-render", "off",
39                                             "How to render bumps",
40                                             "off,normals,normals-tangent,height,high-poly");
41 }
42 
~SceneBump()43 SceneBump::~SceneBump()
44 {
45 }
46 
47 bool
load()48 SceneBump::load()
49 {
50     rotationSpeed_ = 36.0f;
51 
52     running_ = false;
53 
54     return true;
55 }
56 
57 void
unload()58 SceneBump::unload()
59 {
60 }
61 
62 bool
setup_model_plain(const std::string & type)63 SceneBump::setup_model_plain(const std::string &type)
64 {
65     static const std::string vtx_shader_filename(Options::data_path + "/shaders/bump-poly.vert");
66     static const std::string frg_shader_filename(Options::data_path + "/shaders/bump-poly.frag");
67     static const std::string low_poly_filename("asteroid-low");
68     static const std::string high_poly_filename("asteroid-high");
69     static const LibMatrix::vec4 lightPosition(20.0f, 20.0f, 10.0f, 1.0f);
70     Model model;
71 
72     /* Calculate the half vector */
73     LibMatrix::vec3 halfVector(lightPosition.x(), lightPosition.y(), lightPosition.z());
74     halfVector.normalize();
75     halfVector += LibMatrix::vec3(0.0, 0.0, 1.0);
76     halfVector.normalize();
77 
78     std::string poly_filename = type == "high-poly" ?
79                                 high_poly_filename : low_poly_filename;
80 
81     if(!model.load(poly_filename))
82         return false;
83 
84     if (model.needNormals())
85         model.calculate_normals();
86 
87     /* Tell the converter that we only care about position and normal attributes */
88     std::vector<std::pair<Model::AttribType, int> > attribs;
89     attribs.push_back(std::pair<Model::AttribType, int>(Model::AttribTypePosition, 3));
90     attribs.push_back(std::pair<Model::AttribType, int>(Model::AttribTypeNormal, 3));
91 
92     model.convert_to_mesh(mesh_, attribs);
93 
94     /* Load shaders */
95     ShaderSource vtx_source(vtx_shader_filename);
96     ShaderSource frg_source(frg_shader_filename);
97 
98     /* Add constants to shaders */
99     frg_source.add_const("LightSourcePosition", lightPosition);
100     frg_source.add_const("LightSourceHalfVector", halfVector);
101 
102     if (!Scene::load_shaders_from_strings(program_, vtx_source.str(),
103                                           frg_source.str()))
104     {
105         return false;
106     }
107 
108     std::vector<GLint> attrib_locations;
109     attrib_locations.push_back(program_["position"].location());
110     attrib_locations.push_back(program_["normal"].location());
111     mesh_.set_attrib_locations(attrib_locations);
112 
113     return true;
114 }
115 
116 bool
setup_model_normals()117 SceneBump::setup_model_normals()
118 {
119     static const std::string vtx_shader_filename(Options::data_path + "/shaders/bump-normals.vert");
120     static const std::string frg_shader_filename(Options::data_path + "/shaders/bump-normals.frag");
121     static const LibMatrix::vec4 lightPosition(20.0f, 20.0f, 10.0f, 1.0f);
122     Model model;
123 
124     if(!model.load("asteroid-low"))
125         return false;
126 
127     /* Calculate the half vector */
128     LibMatrix::vec3 halfVector(lightPosition.x(), lightPosition.y(), lightPosition.z());
129     halfVector.normalize();
130     halfVector += LibMatrix::vec3(0.0, 0.0, 1.0);
131     halfVector.normalize();
132 
133     /*
134      * We don't care about the vertex normals. We are using a per-fragment
135      * normal map (in object space coordinates).
136      */
137     std::vector<std::pair<Model::AttribType, int> > attribs;
138     attribs.push_back(std::pair<Model::AttribType, int>(Model::AttribTypePosition, 3));
139     attribs.push_back(std::pair<Model::AttribType, int>(Model::AttribTypeTexcoord, 2));
140 
141     model.convert_to_mesh(mesh_, attribs);
142 
143     /* Load shaders */
144     ShaderSource vtx_source(vtx_shader_filename);
145     ShaderSource frg_source(frg_shader_filename);
146 
147     /* Add constants to shaders */
148     frg_source.add_const("LightSourcePosition", lightPosition);
149     frg_source.add_const("LightSourceHalfVector", halfVector);
150 
151     if (!Scene::load_shaders_from_strings(program_, vtx_source.str(),
152                                           frg_source.str()))
153     {
154         return false;
155     }
156 
157     std::vector<GLint> attrib_locations;
158     attrib_locations.push_back(program_["position"].location());
159     attrib_locations.push_back(program_["texcoord"].location());
160     mesh_.set_attrib_locations(attrib_locations);
161 
162     if (!Texture::load("asteroid-normal-map", &texture_,
163                        GL_NEAREST, GL_NEAREST, 0))
164     {
165         return false;
166     }
167 
168     return true;
169 }
170 
171 bool
setup_model_normals_tangent()172 SceneBump::setup_model_normals_tangent()
173 {
174     static const std::string vtx_shader_filename(Options::data_path + "/shaders/bump-normals-tangent.vert");
175     static const std::string frg_shader_filename(Options::data_path + "/shaders/bump-normals-tangent.frag");
176     static const LibMatrix::vec4 lightPosition(20.0f, 20.0f, 10.0f, 1.0f);
177     Model model;
178 
179     if(!model.load("asteroid-low"))
180         return false;
181 
182     if (model.needNormals())
183         model.calculate_normals();
184 
185     /* Calculate the half vector */
186     LibMatrix::vec3 halfVector(lightPosition.x(), lightPosition.y(), lightPosition.z());
187     halfVector.normalize();
188     halfVector += LibMatrix::vec3(0.0, 0.0, 1.0);
189     halfVector.normalize();
190 
191     std::vector<std::pair<Model::AttribType, int> > attribs;
192     attribs.push_back(std::pair<Model::AttribType, int>(Model::AttribTypePosition, 3));
193     attribs.push_back(std::pair<Model::AttribType, int>(Model::AttribTypeNormal, 3));
194     attribs.push_back(std::pair<Model::AttribType, int>(Model::AttribTypeTexcoord, 2));
195     attribs.push_back(std::pair<Model::AttribType, int>(Model::AttribTypeTangent, 3));
196 
197     model.convert_to_mesh(mesh_, attribs);
198 
199     /* Load shaders */
200     ShaderSource vtx_source(vtx_shader_filename);
201     ShaderSource frg_source(frg_shader_filename);
202 
203     /* Add constants to shaders */
204     frg_source.add_const("LightSourcePosition", lightPosition);
205     frg_source.add_const("LightSourceHalfVector", halfVector);
206 
207     if (!Scene::load_shaders_from_strings(program_, vtx_source.str(),
208                                           frg_source.str()))
209     {
210         return false;
211     }
212 
213     std::vector<GLint> attrib_locations;
214     attrib_locations.push_back(program_["position"].location());
215     attrib_locations.push_back(program_["normal"].location());
216     attrib_locations.push_back(program_["texcoord"].location());
217     attrib_locations.push_back(program_["tangent"].location());
218     mesh_.set_attrib_locations(attrib_locations);
219 
220     if (!Texture::load("asteroid-normal-map-tangent", &texture_,
221                        GL_NEAREST, GL_NEAREST, 0))
222     {
223         return false;
224     }
225 
226     return true;
227 }
228 
229 bool
setup_model_height()230 SceneBump::setup_model_height()
231 {
232     static const std::string vtx_shader_filename(Options::data_path + "/shaders/bump-height.vert");
233     static const std::string frg_shader_filename(Options::data_path + "/shaders/bump-height.frag");
234     static const LibMatrix::vec4 lightPosition(20.0f, 20.0f, 10.0f, 1.0f);
235     Model model;
236 
237     if(!model.load("asteroid-low"))
238         return false;
239 
240     if (model.needNormals())
241         model.calculate_normals();
242 
243     /* Calculate the half vector */
244     LibMatrix::vec3 halfVector(lightPosition.x(), lightPosition.y(), lightPosition.z());
245     halfVector.normalize();
246     halfVector += LibMatrix::vec3(0.0, 0.0, 1.0);
247     halfVector.normalize();
248 
249     std::vector<std::pair<Model::AttribType, int> > attribs;
250     attribs.push_back(std::pair<Model::AttribType, int>(Model::AttribTypePosition, 3));
251     attribs.push_back(std::pair<Model::AttribType, int>(Model::AttribTypeNormal, 3));
252     attribs.push_back(std::pair<Model::AttribType, int>(Model::AttribTypeTexcoord, 2));
253     attribs.push_back(std::pair<Model::AttribType, int>(Model::AttribTypeTangent, 3));
254 
255     model.convert_to_mesh(mesh_, attribs);
256 
257     /* Load shaders */
258     ShaderSource vtx_source(vtx_shader_filename);
259     ShaderSource frg_source(frg_shader_filename);
260 
261     /* Add constants to shaders */
262     frg_source.add_const("LightSourcePosition", lightPosition);
263     frg_source.add_const("LightSourceHalfVector", halfVector);
264     frg_source.add_const("TextureStepX", 1.0 / 1024.0);
265     frg_source.add_const("TextureStepY", 1.0 / 1024.0);
266 
267     if (!Scene::load_shaders_from_strings(program_, vtx_source.str(),
268                                           frg_source.str()))
269     {
270         return false;
271     }
272 
273     std::vector<GLint> attrib_locations;
274     attrib_locations.push_back(program_["position"].location());
275     attrib_locations.push_back(program_["normal"].location());
276     attrib_locations.push_back(program_["texcoord"].location());
277     attrib_locations.push_back(program_["tangent"].location());
278     mesh_.set_attrib_locations(attrib_locations);
279 
280     if (!Texture::load("asteroid-height-map", &texture_,
281                        GL_NEAREST, GL_NEAREST, 0))
282     {
283         return false;
284     }
285 
286     return true;
287 }
288 
289 bool
setup()290 SceneBump::setup()
291 {
292     if (!Scene::setup())
293         return false;
294 
295     const std::string &bump_render = options_["bump-render"].value;
296     Texture::find_textures();
297     Model::find_models();
298 
299     bool setup_succeeded = false;
300 
301     if (bump_render == "normals")
302         setup_succeeded = setup_model_normals();
303     else if (bump_render == "normals-tangent")
304         setup_succeeded = setup_model_normals_tangent();
305     else if (bump_render == "height")
306         setup_succeeded = setup_model_height();
307     else if (bump_render == "off" || bump_render == "high-poly")
308         setup_succeeded = setup_model_plain(bump_render);
309 
310     if (!setup_succeeded)
311         return false;
312 
313     mesh_.build_vbo();
314 
315     program_.start();
316 
317     // Load texture sampler value
318     program_["NormalMap"] = 0;
319     program_["HeightMap"] = 0;
320 
321     currentFrame_ = 0;
322     rotation_ = 0.0;
323     running_ = true;
324     startTime_ = Util::get_timestamp_us() / 1000000.0;
325     lastUpdateTime_ = startTime_;
326 
327     return true;
328 }
329 
330 void
teardown()331 SceneBump::teardown()
332 {
333     mesh_.reset();
334 
335     program_.stop();
336     program_.release();
337 
338     glDeleteTextures(1, &texture_);
339     texture_ = 0;
340 
341     Scene::teardown();
342 }
343 
344 void
update()345 SceneBump::update()
346 {
347     Scene::update();
348 
349     double elapsed_time = lastUpdateTime_ - startTime_;
350 
351     rotation_ = rotationSpeed_ * elapsed_time;
352 }
353 
354 void
draw()355 SceneBump::draw()
356 {
357     LibMatrix::Stack4 model_view;
358 
359     // Load the ModelViewProjectionMatrix uniform in the shader
360     LibMatrix::mat4 model_view_proj(canvas_.projection());
361 
362     model_view.translate(0.0f, 0.0f, -3.5f);
363     model_view.rotate(rotation_, 0.0f, 1.0f, 0.0f);
364     model_view_proj *= model_view.getCurrent();
365 
366     program_["ModelViewProjectionMatrix"] = model_view_proj;
367 
368     // Load the NormalMatrix uniform in the shader. The NormalMatrix is the
369     // inverse transpose of the model view matrix.
370     LibMatrix::mat4 normal_matrix(model_view.getCurrent());
371     normal_matrix.inverse().transpose();
372     program_["NormalMatrix"] = normal_matrix;
373 
374     glActiveTexture(GL_TEXTURE0);
375     glBindTexture(GL_TEXTURE_2D, texture_);
376 
377     mesh_.render_vbo();
378 }
379 
380 Scene::ValidationResult
validate()381 SceneBump::validate()
382 {
383     static const double radius_3d(std::sqrt(3.0));
384 
385     if (rotation_ != 0)
386         return Scene::ValidationUnknown;
387 
388     Canvas::Pixel ref;
389 
390     Canvas::Pixel pixel = canvas_.read_pixel(canvas_.width() / 2,
391                                              canvas_.height() / 2);
392 
393     const std::string &bump_render = options_["bump-render"].value;
394 
395     if (bump_render == "off")
396         ref = Canvas::Pixel(0x81, 0x81, 0x81, 0xff);
397     else if (bump_render == "high-poly")
398         ref = Canvas::Pixel(0x9c, 0x9c, 0x9c, 0xff);
399     else if (bump_render == "normals")
400         ref = Canvas::Pixel(0xa4, 0xa4, 0xa4, 0xff);
401     else if (bump_render == "normals-tangent")
402         ref = Canvas::Pixel(0x99, 0x99, 0x99, 0xff);
403     else if (bump_render == "height")
404         ref = Canvas::Pixel(0x9d, 0x9d, 0x9d, 0xff);
405     else
406         return Scene::ValidationUnknown;
407 
408     double dist = pixel.distance_rgb(ref);
409 
410     if (dist < radius_3d + 0.01) {
411         return Scene::ValidationSuccess;
412     }
413     else {
414         Log::debug("Validation failed! Expected: 0x%x Actual: 0x%x Distance: %f\n",
415                     ref.to_le32(), pixel.to_le32(), dist);
416         return Scene::ValidationFailure;
417     }
418 }
419