1 //
2 // Copyright © 2012 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 //  Jesse Barker
21 //
22 #include "scene.h"
23 #include "model.h"
24 #include "options.h"
25 #include "util.h"
26 #include "log.h"
27 #include "shader-source.h"
28 #include "stack.h"
29 
30 using std::string;
31 using std::vector;
32 using std::map;
33 using LibMatrix::Stack4;
34 using LibMatrix::mat4;
35 using LibMatrix::vec4;
36 using LibMatrix::vec3;
37 using LibMatrix::vec2;
38 
39 static const vec4 lightPosition(0.0f, 3.0f, 2.0f, 1.0f);
40 
41 //
42 // To create a shadow map, we need a framebuffer object set up for a
43 // depth-only pass.  The render target can then be bound as a texture,
44 // and the depth values sampled from that texture can be used in the
45 // distance-from-light computations when rendering the shadow on the
46 // ground below the rendered object.
47 //
48 class DepthRenderTarget
49 {
50     Program program_;
51     unsigned int canvas_width_;
52     unsigned int canvas_height_;
53     unsigned int width_;
54     unsigned int height_;
55     unsigned int tex_;
56     unsigned int fbo_;
57     unsigned int canvas_fbo_;
58 public:
DepthRenderTarget()59     DepthRenderTarget() :
60         canvas_width_(0),
61         canvas_height_(0),
62         width_(0),
63         height_(0),
64         tex_(0),
65         fbo_(0) {}
~DepthRenderTarget()66     ~DepthRenderTarget() {}
67     bool setup(unsigned int canvas_fbo, unsigned int width, unsigned int height);
68     void teardown();
69     void enable(const mat4& mvp);
70     void disable();
texture()71     unsigned int texture() { return tex_; }
program()72     Program& program() { return program_; }
73 };
74 
75 bool
setup(unsigned int canvas_fbo,unsigned int width,unsigned int height)76 DepthRenderTarget::setup(unsigned int canvas_fbo, unsigned int width, unsigned int height)
77 {
78     static const string vtx_shader_filename(Options::data_path + "/shaders/depth.vert");
79     static const string frg_shader_filename(Options::data_path + "/shaders/depth.frag");
80 
81     ShaderSource vtx_source(vtx_shader_filename);
82     ShaderSource frg_source(frg_shader_filename);
83 
84     if (!Scene::load_shaders_from_strings(program_, vtx_source.str(), frg_source.str())) {
85         return false;
86     }
87 
88     canvas_width_ = width;
89     canvas_height_ = height;
90     canvas_fbo_ = canvas_fbo;
91     width_ = canvas_width_ * 2;
92     height_ = canvas_height_ * 2;
93 
94     // If the texture will be too large for the implemnetation, we need to
95     // clamp the dimensions but maintain the aspect ratio.
96     GLint tex_size(0);
97     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &tex_size);
98     unsigned int max_size = static_cast<unsigned int>(tex_size);
99     if (max_size < width_ || max_size < height_) {
100         float aspect = static_cast<float>(width) / static_cast<float>(height);
101         width_ = max_size;
102         height_ = width_ / aspect;
103         Log::debug("DepthRenderTarget::setup: original texture size (%u x %u), clamped to (%u x %u)\n",
104             canvas_width_ * 2, canvas_height_ * 2, width_, height_);
105     }
106 
107     glGenTextures(1, &tex_);
108     glBindTexture(GL_TEXTURE_2D, tex_);
109     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
110     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
111     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
112     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
113     glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width_, height_, 0,
114                  GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 0);
115     glBindTexture(GL_TEXTURE_2D, 0);
116 
117     GLExtensions::GenFramebuffers(1, &fbo_);
118     GLExtensions::BindFramebuffer(GL_FRAMEBUFFER, fbo_);
119     GLExtensions::FramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
120                                        tex_, 0);
121     unsigned int status = GLExtensions::CheckFramebufferStatus(GL_FRAMEBUFFER);
122     if (status != GL_FRAMEBUFFER_COMPLETE) {
123         Log::error("DepthRenderTarget::setup: glCheckFramebufferStatus failed (0x%x)\n", status);
124         return false;
125     }
126     GLExtensions::BindFramebuffer(GL_FRAMEBUFFER, canvas_fbo_);
127 
128     return true;
129 }
130 
131 void
teardown()132 DepthRenderTarget::teardown()
133 {
134     program_.stop();
135     program_.release();
136     if (tex_) {
137         glDeleteTextures(1, &tex_);
138         tex_ = 0;
139     }
140     if (fbo_) {
141         GLExtensions::DeleteFramebuffers(1, &fbo_);
142         fbo_ = 0;
143     }
144 }
145 
146 void
enable(const mat4 & mvp)147 DepthRenderTarget::enable(const mat4& mvp)
148 {
149     program_.start();
150     program_["ModelViewProjectionMatrix"] = mvp;
151     GLExtensions::BindFramebuffer(GL_FRAMEBUFFER, fbo_);
152     GLExtensions::FramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
153                            tex_, 0);
154     glViewport(0, 0, width_, height_);
155     glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
156     glClear(GL_DEPTH_BUFFER_BIT);
157 }
158 
disable()159 void DepthRenderTarget::disable()
160 {
161     GLExtensions::BindFramebuffer(GL_FRAMEBUFFER, canvas_fbo_);
162     glViewport(0, 0, canvas_width_, canvas_height_);
163     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
164 }
165 
166 //
167 // The actual shadow pass is really just a quad projected into the scene
168 // with the horse's shadow cast upon it.  In the vertex stage, we compute
169 // a texture coordinate for the depth texture look-up by transforming the
170 // current vertex position using a matrix describing the light's view point.
171 // In the fragment stage, that coordinate is perspective corrected, and
172 // used to sample the depth texture.  If the depth value for that fragment
173 // (effectively the distance from the light to the object at that point)
174 // is less than the Z component of that coordinate (effectively the distance
175 // from the light to the ground at that point) then that location is in shadow.
176 //
177 class GroundRenderer
178 {
179     Program program_;
180     mat4 light_;
181     Stack4 modelview_;
182     mat4 projection_;
183     int positionLocation_;
184     unsigned int bufferObject_;
185     unsigned int texture_;
186     vector<vec2> vertices_;
187     vector<vec2> texcoords_;
188 
189 public:
GroundRenderer()190     GroundRenderer() :
191         positionLocation_(0),
192         bufferObject_(0) {}
~GroundRenderer()193     ~GroundRenderer() {}
194     bool setup(const mat4& projection, unsigned int texture);
195     void teardown();
196     void draw();
197 };
198 
199 bool
setup(const mat4 & projection,unsigned int texture)200 GroundRenderer::setup(const mat4& projection, unsigned int texture)
201 {
202     projection_ = projection;
203     texture_ = texture;
204 
205     // Program set up
206     static const vec4 materialDiffuse(0.3f, 0.3f, 0.3f, 1.0f);
207     static const string vtx_shader_filename(Options::data_path + "/shaders/shadow.vert");
208     static const string frg_shader_filename(Options::data_path + "/shaders/shadow.frag");
209     ShaderSource vtx_source(vtx_shader_filename);
210     ShaderSource frg_source(frg_shader_filename);
211 
212     vtx_source.add_const("MaterialDiffuse", materialDiffuse);
213 
214     if (!Scene::load_shaders_from_strings(program_, vtx_source.str(), frg_source.str())) {
215         return false;
216     }
217     positionLocation_ = program_["position"].location();
218 
219     // Set up the position data for our "quad".
220     vertices_.push_back(vec2(-1.0, -1.0));
221     vertices_.push_back(vec2(1.0, -1.0));
222     vertices_.push_back(vec2(-1.0, 1.0));
223     vertices_.push_back(vec2(1.0, 1.0));
224 
225     // Set up the VBO and stash our position data in it.
226     glGenBuffers(1, &bufferObject_);
227     glBindBuffer(GL_ARRAY_BUFFER, bufferObject_);
228     glBufferData(GL_ARRAY_BUFFER, vertices_.size() * sizeof(vec2),
229                  &vertices_.front(), GL_STATIC_DRAW);
230     glBindBuffer(GL_ARRAY_BUFFER, 0);
231 
232     // Set up the light matrix with a bias that will convert values
233     // in the range of [-1, 1] to [0, 1)], then add in the projection
234     // and the "look at" matrix from the light position.
235     light_ *= LibMatrix::Mat4::translate(0.5, 0.5, 0.5);
236     light_ *= LibMatrix::Mat4::scale(0.5, 0.5, 0.5);
237     light_ *= projection_;
238     light_ *= LibMatrix::Mat4::lookAt(lightPosition.x(), lightPosition.y(), lightPosition.z(),
239                                       0.0, 0.0, 0.0,
240                                       0.0, 1.0, 0.0);
241 
242     return true;
243 }
244 
245 void
teardown()246 GroundRenderer::teardown()
247 {
248     program_.stop();
249     program_.release();
250     glBindBuffer(GL_ARRAY_BUFFER, 0);
251     glDeleteBuffers(1, &bufferObject_);
252     bufferObject_ = 0;
253     texture_= 0;
254     vertices_.clear();
255 }
256 
257 void
draw()258 GroundRenderer::draw()
259 {
260     // Need to add uniforms for the shadow texture, and transformation to
261     // "lay the quad down".
262     modelview_.push();
263     modelview_.translate(projection_[0][3], projection_[1][3] - 0.8, projection_[2][3] - 0.5);
264     modelview_.rotate(-85.0, 1.0, 0.0, 0.0);
265     modelview_.scale(2.0, 2.0, 2.0);
266     mat4 mvp(projection_);
267     mvp *= modelview_.getCurrent();
268 
269     glBindBuffer(GL_ARRAY_BUFFER, bufferObject_);
270     program_.start();
271     glActiveTexture(GL_TEXTURE0);
272     glBindTexture(GL_TEXTURE_2D, texture_);
273     program_["ShadowMap"] = 0;
274     program_["LightMatrix"] = light_;
275     program_["ModelViewProjectionMatrix"] = mvp;
276 
277     glEnableVertexAttribArray(positionLocation_);
278     glVertexAttribPointer(positionLocation_, 2, GL_FLOAT, GL_FALSE, 0, 0);
279     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
280     glDisableVertexAttribArray(positionLocation_);
281     glBindTexture(GL_TEXTURE_2D, 0);
282     glBindBuffer(GL_ARRAY_BUFFER, 0);
283     modelview_.pop();
284 }
285 
286 class ShadowPrivate
287 {
288     Canvas& canvas_;
289     DepthRenderTarget depthTarget_;
290     GroundRenderer ground_;
291     Program program_;
292     Stack4 modelview_;
293     Stack4 projection_;
294     Mesh mesh_;
295     vec3 centerVec_;
296     float radius_;
297     float rotation_;
298     float rotationSpeed_;
299     bool useVbo_;
300 
301 public:
ShadowPrivate(Canvas & canvas)302     ShadowPrivate(Canvas& canvas) :
303         canvas_(canvas),
304         radius_(0.0),
305         rotation_(0.0),
306         rotationSpeed_(36.0),
307         useVbo_(true) {}
~ShadowPrivate()308     ~ShadowPrivate() {}
309 
310     bool setup(map<string, Scene::Option>& options);
311     void teardown();
312     void update(double elapsedTime);
313     void draw();
314 };
315 
316 bool
setup(map<string,Scene::Option> & options)317 ShadowPrivate::setup(map<string, Scene::Option>& options)
318 {
319     // Program object setup
320     static const string vtx_shader_filename(Options::data_path + "/shaders/light-basic.vert");
321     static const string frg_shader_filename(Options::data_path + "/shaders/light-basic.frag");
322     static const vec4 materialDiffuse(1.0f, 1.0f, 1.0f, 1.0f);
323 
324     ShaderSource vtx_source(vtx_shader_filename);
325     ShaderSource frg_source(frg_shader_filename);
326 
327     vtx_source.add_const("LightSourcePosition", lightPosition);
328     vtx_source.add_const("MaterialDiffuse", materialDiffuse);
329 
330     if (!Scene::load_shaders_from_strings(program_, vtx_source.str(), frg_source.str())) {
331         return false;
332     }
333 
334     // Model setup
335     Model::find_models();
336     Model model;
337     bool modelLoaded = model.load("horse");
338 
339     if(!modelLoaded) {
340         return false;
341     }
342 
343     if (model.needNormals())
344         model.calculate_normals();
345 
346     // Mesh setup
347     vector<std::pair<Model::AttribType, int> > attribs;
348     attribs.push_back(std::pair<Model::AttribType, int>(Model::AttribTypePosition, 3));
349     attribs.push_back(std::pair<Model::AttribType, int>(Model::AttribTypeNormal, 3));
350     model.convert_to_mesh(mesh_, attribs);
351 
352     useVbo_ = (options["use-vbo"].value == "true");
353     bool interleave = (options["interleave"].value == "true");
354     mesh_.vbo_update_method(Mesh::VBOUpdateMethodMap);
355     mesh_.interleave(interleave);
356 
357     if (useVbo_) {
358         mesh_.build_vbo();
359     }
360     else {
361         mesh_.build_array();
362     }
363 
364     // Calculate a projection matrix that is a good fit for the model
365     vec3 maxVec = model.maxVec();
366     vec3 minVec = model.minVec();
367     vec3 diffVec = maxVec - minVec;
368     centerVec_ = maxVec + minVec;
369     centerVec_ /= 2.0;
370     float diameter = diffVec.length();
371     radius_ = diameter / 2;
372     float fovy = 2.0 * atanf(radius_ / (2.0 + radius_));
373     fovy /= M_PI;
374     fovy *= 180.0;
375     float aspect(static_cast<float>(canvas_.width())/static_cast<float>(canvas_.height()));
376     projection_.perspective(fovy, aspect, 2.0, 50.0);
377 
378     if (!depthTarget_.setup(canvas_.fbo(), canvas_.width(), canvas_.height())) {
379         Log::error("Failed to set up the render target for the depth pass\n");
380         return false;
381     }
382 
383     if (!ground_.setup(projection_.getCurrent(), depthTarget_.texture())) {
384         Log::error("Failed to set up the ground renderer\n");
385         return false;
386     }
387 
388     return true;
389 }
390 
391 
392 void
teardown()393 ShadowPrivate::teardown()
394 {
395     depthTarget_.teardown();
396     ground_.teardown();
397     program_.stop();
398     program_.release();
399     mesh_.reset();
400 }
401 
402 void
update(double elapsedTime)403 ShadowPrivate::update(double elapsedTime)
404 {
405     rotation_ = rotationSpeed_ * elapsedTime;
406 }
407 
408 void
draw()409 ShadowPrivate::draw()
410 {
411     // To perform the depth pass, set up the model-view transformation so
412     // that we're looking at the horse from the light position.  That will
413     // give us the appropriate view for the shadow.
414     modelview_.push();
415     modelview_.loadIdentity();
416     modelview_.lookAt(lightPosition.x(), lightPosition.y(), lightPosition.z(),
417                       0.0, 0.0, 0.0,
418                       0.0, 1.0, 0.0);
419     modelview_.rotate(rotation_, 0.0f, 1.0f, 0.0f);
420     mat4 mvp(projection_.getCurrent());
421     mvp *= modelview_.getCurrent();
422     modelview_.pop();
423 
424     // Enable the depth render target with our transformation and render.
425     depthTarget_.enable(mvp);
426     vector<GLint> attrib_locations;
427     attrib_locations.push_back(depthTarget_.program()["position"].location());
428     attrib_locations.push_back(depthTarget_.program()["normal"].location());
429     mesh_.set_attrib_locations(attrib_locations);
430     if (useVbo_) {
431         mesh_.render_vbo();
432     }
433     else {
434         mesh_.render_array();
435     }
436     depthTarget_.disable();
437 
438     // Ground rendering using the above generated texture...
439     ground_.draw();
440 
441     // Draw the "normal" view of the horse
442     modelview_.push();
443     modelview_.translate(-centerVec_.x(), -centerVec_.y(), -(centerVec_.z() + 2.0 + radius_));
444     modelview_.rotate(rotation_, 0.0f, 1.0f, 0.0f);
445     mvp = projection_.getCurrent();
446     mvp *= modelview_.getCurrent();
447 
448     program_.start();
449     program_["ModelViewProjectionMatrix"] = mvp;
450 
451     // Load the NormalMatrix uniform in the shader. The NormalMatrix is the
452     // inverse transpose of the model view matrix.
453     LibMatrix::mat4 normal_matrix(modelview_.getCurrent());
454     normal_matrix.inverse().transpose();
455     program_["NormalMatrix"] = normal_matrix;
456     attrib_locations.clear();
457     attrib_locations.push_back(program_["position"].location());
458     attrib_locations.push_back(program_["normal"].location());
459     mesh_.set_attrib_locations(attrib_locations);
460     if (useVbo_) {
461         mesh_.render_vbo();
462     }
463     else {
464         mesh_.render_array();
465     }
466 
467     // Per-frame cleanup
468     modelview_.pop();
469 }
470 
SceneShadow(Canvas & canvas)471 SceneShadow::SceneShadow(Canvas& canvas) :
472     Scene(canvas, "shadow"),
473     priv_(0)
474 {
475     options_["use-vbo"] = Scene::Option("use-vbo", "true",
476                                         "Whether to use VBOs for rendering",
477                                         "false,true");
478     options_["interleave"] = Scene::Option("interleave", "false",
479                                            "Whether to interleave vertex attribute data",
480                                            "false,true");
481 }
482 
483 bool
supported(bool show_errors)484 SceneShadow::supported(bool show_errors)
485 {
486     static const string oes_depth_texture("GL_OES_depth_texture");
487     static const string arb_depth_texture("GL_ARB_depth_texture");
488     bool ret = true;
489 
490     if (!GLExtensions::support(oes_depth_texture) &&
491         !GLExtensions::support(arb_depth_texture)) {
492         if (show_errors) {
493             Log::error("We do not have the depth texture extension!!!\n");
494         }
495 
496         ret = false;
497     }
498 
499     if (!GLExtensions::GenFramebuffers) {
500         if (show_errors)
501             Log::error("SceneShadow requires GL framebuffer support\n");
502         ret = false;
503     }
504 
505     return ret;
506 }
507 
508 bool
load()509 SceneShadow::load()
510 {
511     running_ = false;
512     return true;
513 }
514 
515 void
unload()516 SceneShadow::unload()
517 {
518 }
519 
520 bool
setup()521 SceneShadow::setup()
522 {
523     // If the scene isn't supported, don't bother to go through setup.
524     if (!Scene::setup())
525         return false;
526 
527     priv_ = new ShadowPrivate(canvas_);
528     if (!priv_->setup(options_)) {
529         delete priv_;
530         priv_ = 0;
531         return false;
532     }
533 
534     // Set core scene timing after actual initialization so we don't measure
535     // set up time.
536     startTime_ = Util::get_timestamp_us() / 1000000.0;
537     lastUpdateTime_ = startTime_;
538     running_ = true;
539 
540     return true;
541 }
542 
543 void
teardown()544 SceneShadow::teardown()
545 {
546     // Add scene-specific teardown here
547     if (priv_) {
548         priv_->teardown();
549         delete priv_;
550     }
551     Scene::teardown();
552 }
553 
554 void
update()555 SceneShadow::update()
556 {
557     Scene::update();
558     // Add scene-specific update here
559     priv_->update(lastUpdateTime_ - startTime_);
560 }
561 
562 void
draw()563 SceneShadow::draw()
564 {
565     priv_->draw();
566 }
567 
568 Scene::ValidationResult
validate()569 SceneShadow::validate()
570 {
571     return Scene::ValidationUnknown;
572 }
573