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 //  Aleksandar Rodic - Creator and WebGL implementation
21 //  Jesse Barker - glmark2 port
22 //
23 #include <string>
24 #include <fstream>
25 #include <memory>
26 #include <iomanip>
27 #include "options.h"
28 #include "scene.h"
29 #include "scene-jellyfish.h"
30 #include "log.h"
31 #include "util.h"
32 #include "texture.h"
33 #include "shader-source.h"
34 
SceneJellyfish(Canvas & canvas)35 SceneJellyfish::SceneJellyfish(Canvas& canvas) :
36     Scene(canvas, "jellyfish"), priv_(0)
37 {
38 
39 }
40 
~SceneJellyfish()41 SceneJellyfish::~SceneJellyfish()
42 {
43     delete priv_;
44 }
45 
46 bool
load()47 SceneJellyfish::load()
48 {
49     running_ = false;
50     return true;
51 }
52 
53 void
unload()54 SceneJellyfish::unload()
55 {
56 }
57 
58 bool
setup()59 SceneJellyfish::setup()
60 {
61     if (!Scene::setup())
62         return false;
63 
64     // Set up our private object that does all of the lifting
65     priv_ = new JellyfishPrivate();
66     if (!priv_->initialize())
67         return false;
68 
69     // Set core scene timing after actual initialization so we don't measure
70     // set up time.
71     startTime_ = Util::get_timestamp_us() / 1000000.0;
72     lastUpdateTime_ = startTime_;
73     running_ = true;
74 
75     return true;
76 }
77 
78 void
teardown()79 SceneJellyfish::teardown()
80 {
81     priv_->cleanup();
82     Scene::teardown();
83 }
84 
85 void
update()86 SceneJellyfish::update()
87 {
88     Scene::update();
89     priv_->update_viewport(LibMatrix::vec2(canvas_.width(), canvas_.height()));
90     priv_->update_time();
91 }
92 
93 void
draw()94 SceneJellyfish::draw()
95 {
96     priv_->draw();
97 }
98 
99 Scene::ValidationResult
validate()100 SceneJellyfish::validate()
101 {
102     return Scene::ValidationUnknown;
103 }
104 
105 
106 //
107 // JellyfishPrivate implementation
108 //
109 using LibMatrix::mat4;
110 using LibMatrix::vec3;
111 using LibMatrix::vec2;
112 using std::string;
113 using std::vector;
114 
115 bool
init()116 GradientRenderer::init()
117 {
118     // Program set up
119     static const string vtx_shader_filename(Options::data_path + "/shaders/gradient.vert");
120     static const string frg_shader_filename(Options::data_path + "/shaders/gradient.frag");
121     ShaderSource vtx_source(vtx_shader_filename);
122     ShaderSource frg_source(frg_shader_filename);
123     if (!Scene::load_shaders_from_strings(program_, vtx_source.str(),
124         frg_source.str()))
125     {
126         return false;
127     }
128     positionLocation_ = program_["position"].location();
129     uvLocation_ = program_["uvIn"].location();
130 
131     // Set up the position data for our "quad".
132     vertices_.push_back(vec2(-1.0, -1.0));
133     vertices_.push_back(vec2(1.0, -1.0));
134     vertices_.push_back(vec2(-1.0, 1.0));
135     vertices_.push_back(vec2(1.0, 1.0));
136     uvs_.push_back(vec2(1.0, 1.0));
137     uvs_.push_back(vec2(1.0, 1.0));
138     uvs_.push_back(vec2(0.0, 0.0));
139     uvs_.push_back(vec2(0.0, 0.0));
140     uvOffset_ = vertices_.size() * sizeof(vec2);
141 
142     // Set up the VBO and stash our position data in it.
143     glGenBuffers(1, &bufferObject_);
144     glBindBuffer(GL_ARRAY_BUFFER, bufferObject_);
145     glBufferData(GL_ARRAY_BUFFER, (vertices_.size() + uvs_.size()) * sizeof(vec2),
146                  0, GL_STATIC_DRAW);
147     glBufferSubData(GL_ARRAY_BUFFER, 0, vertices_.size() * sizeof(vec2),
148                     &vertices_.front());
149     glBufferSubData(GL_ARRAY_BUFFER, uvOffset_, uvs_.size() * sizeof(vec2),
150                     &uvs_.front());
151     glBindBuffer(GL_ARRAY_BUFFER, 0);
152 
153     return true;
154 }
155 
156 void
cleanup()157 GradientRenderer::cleanup()
158 {
159     program_.stop();
160     program_.release();
161     glBindBuffer(GL_ARRAY_BUFFER, 0);
162     glDeleteBuffers(1, &bufferObject_);
163 }
164 
165 void
draw()166 GradientRenderer::draw()
167 {
168     static const vec3 lightBlue(0.360784314, 0.584313725, 1.0);
169     static const vec3 darkBlue(0.074509804, 0.156862745, 0.619607843);
170     glBindBuffer(GL_ARRAY_BUFFER, bufferObject_);
171     program_.start();
172     program_["color1"] = lightBlue;
173     program_["color2"] = darkBlue;
174     glEnableVertexAttribArray(positionLocation_);
175     glEnableVertexAttribArray(uvLocation_);
176     glVertexAttribPointer(positionLocation_, 2, GL_FLOAT, GL_FALSE, 0, 0);
177     glVertexAttribPointer(uvLocation_, 2, GL_FLOAT, GL_FALSE, 0,
178                           reinterpret_cast<GLvoid*>(uvOffset_));
179     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
180     glDisableVertexAttribArray(positionLocation_);
181     glDisableVertexAttribArray(uvLocation_);
182     program_.stop();
183     glBindBuffer(GL_ARRAY_BUFFER, 0);
184 }
185 
186 //!
187 // Parse index values from an OBJ file.
188 //
189 // @param source the source line to parse
190 // @param idx the unsigned short to populate
191 //
192 static void
obj_get_index(const string & source,unsigned short & idx)193 obj_get_index(const string& source, unsigned short& idx)
194 {
195      // Skip the definition type...
196     string::size_type endPos = source.find(" ");
197     string::size_type startPos(0);
198     if (endPos == string::npos)
199     {
200         Log::error("Bad element '%s'\n", source.c_str());
201         return;
202     }
203     // Find the first value...
204     startPos = endPos + 1;
205     string is(source, startPos);
206     idx = Util::fromString<unsigned short>(is);
207 }
208 
209 //!
210 // Parse vec3 values from an OBJ file.
211 //
212 // @param source the source line to parse
213 // @param v the vec3 to populate
214 //
215 static void
obj_get_values(const string & source,vec3 & v)216 obj_get_values(const string& source, vec3& v)
217 {
218     // Skip the definition type...
219     string::size_type endPos = source.find(" ");
220     string::size_type startPos(0);
221     if (endPos == string::npos)
222     {
223         Log::error("Bad element '%s'\n", source.c_str());
224         return;
225     }
226     // Find the first value...
227     startPos = endPos + 1;
228     endPos = source.find(" ", startPos);
229     if (endPos == string::npos)
230     {
231         Log::error("Bad element '%s'\n", source.c_str());
232         return;
233     }
234     string::size_type numChars(endPos - startPos);
235     string xs(source, startPos, numChars);
236     float x = Util::fromString<float>(xs);
237     // Then the second value...
238     startPos = endPos + 1;
239     endPos = source.find(" ", startPos);
240     if (endPos == string::npos)
241     {
242         Log::error("Bad element '%s'\n", source.c_str());
243         return;
244     }
245     numChars = endPos - startPos;
246     string ys(source, startPos, numChars);
247     float y = Util::fromString<float>(ys);
248     // And the third value (there might be a fourth, but we don't care)...
249     startPos = endPos + 1;
250     endPos = source.find(" ", startPos);
251     if (endPos == string::npos)
252     {
253         numChars = endPos;
254     }
255     else
256     {
257         numChars = endPos - startPos;
258     }
259     string zs(source, startPos, endPos - startPos);
260     float z = Util::fromString<float>(zs);
261     v.x(x);
262     v.y(y);
263     v.z(z);
264 }
265 
266 // Custom OBJ loader.
267 //
268 // To support the jellyfish model, some amendments to the OBJ format are
269 // necessary.  In particular, a vertex color attribute is required, and
270 // it contains an index list rather than a face list.
271 bool
load_obj(const std::string & filename)272 JellyfishPrivate::load_obj(const std::string &filename)
273 {
274     Log::debug("Loading model from file '%s'\n", filename.c_str());
275 
276     const std::unique_ptr<std::istream> input_file_ptr(Util::get_resource(filename));
277     std::istream& inputFile(*input_file_ptr);
278     if (!inputFile)
279     {
280         Log::error("Failed to open '%s'\n", filename.c_str());
281         return false;
282     }
283 
284     vector<string> sourceVec;
285     string curLine;
286     while (getline(inputFile, curLine))
287     {
288         sourceVec.push_back(curLine);
289     }
290 
291     static const string vertex_definition("v");
292     static const string normal_definition("vn");
293     static const string texcoord_definition("vt");
294     static const string color_definition("vc");
295     static const string index_definition("i");
296     for (vector<string>::const_iterator lineIt = sourceVec.begin();
297          lineIt != sourceVec.end();
298          lineIt++)
299     {
300         const string& curSrc = *lineIt;
301         // Is it a vertex attribute, a face description, comment or other?
302         // We only care about the first two, we ignore comments, object names,
303         // group names, smoothing groups, etc.
304         string::size_type startPos(0);
305         string::size_type spacePos = curSrc.find(" ", startPos);
306         string definitionType(curSrc, startPos, spacePos - startPos);
307         if (definitionType == vertex_definition)
308         {
309             vec3 v;
310             obj_get_values(curSrc, v);
311             positions_.push_back(v);
312         }
313         else if (definitionType == normal_definition)
314         {
315             vec3 v;
316             obj_get_values(curSrc, v);
317             normals_.push_back(v);
318         }
319         else if (definitionType == color_definition)
320         {
321             vec3 v;
322             obj_get_values(curSrc, v);
323             colors_.push_back(v);
324         }
325         else if (definitionType == texcoord_definition)
326         {
327             vec3 v;
328             obj_get_values(curSrc, v);
329             texcoords_.push_back(v);
330         }
331         else if (definitionType == index_definition)
332         {
333             unsigned short idx(0);
334             obj_get_index(curSrc, idx);
335             indices_.push_back(idx);
336         }
337     }
338 
339     Log::debug("Object populated with %u vertices %u normals %u colors %u texcoords and %u indices.\n",
340         positions_.size(), normals_.size(), colors_.size(), texcoords_.size(), indices_.size());
341     return true;
342 }
343 
JellyfishPrivate()344 JellyfishPrivate::JellyfishPrivate() :
345     positionLocation_(0),
346     normalLocation_(0),
347     colorLocation_(0),
348     texcoordLocation_(0),
349     viewport_(512.0, 512.0),
350     lightPosition_(10.0, 40.0, -60.0),
351     lightColor_(0.8, 1.3, 1.1, 1.0),
352     lightRadius_(200.0),
353     ambientColor_(0.3, 0.2, 1.0, 1.0),
354     fresnelColor_(0.8, 0.7, 0.6, 1.1),
355     fresnelPower_(1.0),
356     rotation_(0.0),
357     currentTime_(0.0),
358     lastUpdateTime_(0.0),
359     cullFace_(0),
360     depthTest_(0),
361     blend_(0),
362     blendFuncSrc_(0),
363     blendFuncDst_(0)
364 {
365 }
366 
~JellyfishPrivate()367 JellyfishPrivate::~JellyfishPrivate()
368 {
369     positions_.clear();
370     normals_.clear();
371     colors_.clear();
372     texcoords_.clear();
373     indices_.clear();
374 }
375 
376 bool
initialize()377 JellyfishPrivate::initialize()
378 {
379     static const string modelFilename(Options::data_path + "/models/jellyfish.jobj");
380     if (!load_obj(modelFilename))
381     {
382         return false;
383     }
384 
385     // Now that we've setup the vertex data, we can setup the map of how
386     // that data will be laid out in the buffer object.
387     static const unsigned int sv3(sizeof(vec3));
388     dataMap_.positionOffset = 0;
389     dataMap_.positionSize = positions_.size() * sv3;
390     dataMap_.totalSize = dataMap_.positionSize;
391     dataMap_.normalOffset = dataMap_.positionOffset + dataMap_.positionSize;
392     dataMap_.normalSize = normals_.size() * sv3;
393     dataMap_.totalSize += dataMap_.normalSize;
394     dataMap_.colorOffset = dataMap_.normalOffset + dataMap_.normalSize;
395     dataMap_.colorSize = colors_.size() * sv3;
396     dataMap_.totalSize += dataMap_.colorSize;
397     dataMap_.texcoordOffset = dataMap_.colorOffset + dataMap_.colorSize;
398     dataMap_.texcoordSize = texcoords_.size() * sv3;
399     dataMap_.totalSize += dataMap_.texcoordSize;
400 
401     lastUpdateTime_ = Util::get_timestamp_us() / 1000.0;
402     currentTime_ = static_cast<uint64_t>(lastUpdateTime_) % 100000000 / 1000.0;
403     whichCaustic_ = static_cast<uint64_t>(currentTime_ * 30) % 32 + 1;
404     rotation_ = 0.0;
405 
406     if (!gradient_.init())
407     {
408         return false;
409     }
410 
411     // Set up program first so we can store attribute and uniform locations
412     // away for the
413     using std::string;
414     static const string vtx_shader_filename(Options::data_path + "/shaders/jellyfish.vert");
415     static const string frg_shader_filename(Options::data_path + "/shaders/jellyfish.frag");
416 
417     ShaderSource vtx_source(vtx_shader_filename);
418     ShaderSource frg_source(frg_shader_filename);
419 
420     // Use high float precision, if available, in the jellyfish fragment shader
421     frg_source.precision(std::string(",high,,"));
422 
423     if (!Scene::load_shaders_from_strings(program_, vtx_source.str(),
424         frg_source.str()))
425     {
426         return false;
427     }
428 
429     // Stash away attribute and uniform locations for handy use.
430     positionLocation_ = program_["aVertexPosition"].location();
431     normalLocation_ = program_["aVertexNormal"].location();
432     colorLocation_ = program_["aVertexColor"].location();
433     texcoordLocation_ = program_["aTextureCoord"].location();
434 
435     // We need 2 buffers for our work here.  One for the vertex data.
436     // and one for the index data.
437     glGenBuffers(2, &bufferObjects_[0]);
438 
439     // First, setup the vertex data by binding the first buffer object,
440     // allocating its data store, and filling it in with our vertex data.
441     glBindBuffer(GL_ARRAY_BUFFER, bufferObjects_[0]);
442     glBufferData(GL_ARRAY_BUFFER, dataMap_.totalSize, 0, GL_STATIC_DRAW);
443     glBufferSubData(GL_ARRAY_BUFFER, dataMap_.positionOffset,
444                     dataMap_.positionSize, &positions_.front());
445     glBufferSubData(GL_ARRAY_BUFFER, dataMap_.normalOffset,
446                     dataMap_.normalSize, &normals_.front());
447     glBufferSubData(GL_ARRAY_BUFFER, dataMap_.colorOffset,
448                     dataMap_.colorSize, &colors_.front());
449     glBufferSubData(GL_ARRAY_BUFFER, dataMap_.texcoordOffset,
450                     dataMap_.texcoordSize, &texcoords_.front());
451 
452     // Now repeat for our index data.
453     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferObjects_[1]);
454     glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices_.size() * sizeof(unsigned short),
455                  &indices_.front(), GL_STATIC_DRAW);
456 
457     // "Unbind" our buffer objects to make sure the state is consistent.
458     glBindBuffer(GL_ARRAY_BUFFER, 0);
459     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
460 
461     // Finally, set up our textures.
462     //
463     // First, the main jellyfish texture
464     bool gotTex = Texture::load("jellyfish256", &textureObjects_[0], GL_LINEAR,
465                                 GL_LINEAR, 0);
466     if (!gotTex || textureObjects_[0] == 0)
467     {
468         Log::error("Jellyfish texture set up failed!!!\n");
469         return false;
470     }
471     glBindTexture(GL_TEXTURE_2D, 0);
472     // Then, the caustics textures
473     static const string baseName("jellyfish-caustics-");
474     for (unsigned int i = 1; i < 33; i++)
475     {
476         std::stringstream ss;
477         ss << std::setw(2) << std::setfill('0') << i;
478         string curName(baseName);
479         curName += ss.str();
480         gotTex = Texture::load(curName, &textureObjects_[i], GL_LINEAR,
481                                GL_LINEAR, 0);
482         if (!gotTex || textureObjects_[i] == 0)
483         {
484             Log::error("Caustics texture[%u] set up failed!!!\n", i);
485             return false;
486         }
487         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
488         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
489         glBindTexture(GL_TEXTURE_2D, 0);
490     }
491 
492     // Save the GL state we are changing so we can restore it later.
493     cullFace_ = glIsEnabled(GL_CULL_FACE);
494     depthTest_ = glIsEnabled(GL_DEPTH_TEST);
495     blend_ = glIsEnabled(GL_BLEND);
496     glGetIntegerv(GL_BLEND_SRC_RGB, &blendFuncSrc_);
497     glGetIntegerv(GL_BLEND_DST_RGB, &blendFuncDst_);
498 
499     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
500     glEnable(GL_BLEND);
501     glDisable(GL_CULL_FACE);
502     glDisable(GL_DEPTH_TEST);
503 
504     return true;
505 }
506 
507 void
update_viewport(const vec2 & vp)508 JellyfishPrivate::update_viewport(const vec2& vp)
509 {
510     if (viewport_.x() == vp.x() && viewport_.y() == vp.y())
511     {
512         return;
513     }
514     viewport_ = vp;
515     projection_.loadIdentity();
516     projection_.perspective(30.0, viewport_.x()/viewport_.y(), 20.0, 120.0);
517 }
518 
519 void
update_time()520 JellyfishPrivate::update_time()
521 {
522     double now = Util::get_timestamp_us() / 1000.0;
523     double elapsedTime = now - lastUpdateTime_;
524     rotation_ += (2.0 * elapsedTime) / 1000.0;
525     currentTime_ = static_cast<uint64_t>(now) % 100000000 / 1000.0;
526     whichCaustic_ = static_cast<uint64_t>(currentTime_ * 30) % 32 + 1;
527     lastUpdateTime_ = now;
528 }
529 
530 void
cleanup()531 JellyfishPrivate::cleanup()
532 {
533     // Restore the GL state we changed for the scene.
534     glBlendFunc(blendFuncSrc_, blendFuncDst_);
535     if (GL_FALSE == blend_)
536     {
537         glDisable(GL_BLEND);
538     }
539     if (GL_TRUE == cullFace_)
540     {
541         glEnable(GL_CULL_FACE);
542     }
543     if (GL_TRUE == depthTest_)
544     {
545         glEnable(GL_DEPTH_TEST);
546     }
547 
548     program_.stop();
549     program_.release();
550 
551     glBindBuffer(GL_ARRAY_BUFFER, 0);
552     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
553 
554     glDeleteTextures(33, &textureObjects_[0]);
555     glDeleteBuffers(2, &bufferObjects_[0]);
556 
557     gradient_.cleanup();
558 }
559 
560 void
draw()561 JellyfishPrivate::draw()
562 {
563     // "Clear" the background to the desired gradient.
564     gradient_.draw();
565 
566     // We need "world", "world view projection", and "world inverse transpose"
567     // matrix uniforms for the current shader.
568     //
569     // NOTE: Some of this seems a bit of a no-op (e.g., multipying by and
570     //       inverting identity matrices), but leave it like the original
571     //       WebGL files for the time being.  Worth revisiting not doing all
572     //       of that math every draw call (might even be good to be doing
573     //       some of it in the shader as well).
574     world_.push();
575     world_.translate(0.0, 5.0, -75.0);
576     world_.rotate(sin(rotation_ / 10.0) * 30.0, 0.0, 1.0, 0.0);
577     world_.rotate(sin(rotation_ / 20.0) * 30.0, 1.0, 0.0, 0.0);
578     world_.scale(5.0, 5.0, 5.0);
579     world_.translate(0.0, sin(rotation_ / 10.0) * 2.5, 0.0);
580     mat4 worldViewProjection(projection_.getCurrent());
581     worldViewProjection *= world_.getCurrent();;
582     mat4 worldInverseTranspose(world_.getCurrent());
583     worldInverseTranspose.inverse().transpose();
584 
585     // Load up the uniforms
586     program_.start();
587     program_["uWorld"] = world_.getCurrent();
588     program_["uWorldViewProj"] = worldViewProjection;
589     program_["uWorldInvTranspose"] = worldInverseTranspose;
590     program_["uCurrentTime"] = currentTime_;
591     // Revisit making these constants rather than uniforms as they appear never
592     // to change
593     program_["uLightPos"] = lightPosition_;
594     program_["uLightRadius"] = lightRadius_;
595     program_["uLightCol"] = lightColor_;
596     program_["uAmbientCol"] = ambientColor_;
597     program_["uFresnelCol"] = fresnelColor_;
598     program_["uFresnelPower"] = fresnelPower_;
599     // Set up textures for this frame.
600     glActiveTexture(GL_TEXTURE0);
601     glBindTexture(GL_TEXTURE_2D, textureObjects_[0]);
602     program_["uSampler"] = 0;
603     glActiveTexture(GL_TEXTURE1);
604     glBindTexture(GL_TEXTURE_2D, textureObjects_[whichCaustic_]);
605     program_["uSampler1"] = 1;
606 
607     glBindBuffer(GL_ARRAY_BUFFER, bufferObjects_[0]);
608     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferObjects_[1]);
609 
610     glEnableVertexAttribArray(positionLocation_);
611     glEnableVertexAttribArray(normalLocation_);
612     glEnableVertexAttribArray(colorLocation_);
613     glEnableVertexAttribArray(texcoordLocation_);
614     glVertexAttribPointer(positionLocation_ , 3, GL_FLOAT, GL_FALSE, 0,
615         reinterpret_cast<const GLvoid*>(dataMap_.positionOffset));
616     glVertexAttribPointer(normalLocation_ , 3, GL_FLOAT, GL_FALSE, 0,
617         reinterpret_cast<const GLvoid*>(dataMap_.normalOffset));
618     glVertexAttribPointer(colorLocation_ , 3, GL_FLOAT, GL_FALSE, 0,
619         reinterpret_cast<const GLvoid*>(dataMap_.colorOffset));
620     glVertexAttribPointer(texcoordLocation_ , 3, GL_FLOAT, GL_FALSE, 0,
621         reinterpret_cast<const GLvoid*>(dataMap_.texcoordOffset));
622 
623     glDrawElements(GL_TRIANGLES, indices_.size(), GL_UNSIGNED_SHORT, 0);
624 
625     glDisableVertexAttribArray(positionLocation_);
626     glDisableVertexAttribArray(normalLocation_);
627     glDisableVertexAttribArray(colorLocation_);
628     glDisableVertexAttribArray(texcoordLocation_);
629 
630     glBindBuffer(GL_ARRAY_BUFFER, 0);
631     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
632 
633     program_.stop();
634     world_.pop();
635 }
636