samples/3D: Added materials sample with reflection, refraction and normals mapping
authorJerome St-Louis <jerome@ecere.com>
Wed, 3 Aug 2016 22:23:30 +0000 (18:23 -0400)
committerJerome St-Louis <jerome@ecere.com>
Wed, 3 Aug 2016 22:23:30 +0000 (18:23 -0400)
samples/3D/materials/materials.ec [new file with mode: 0644]
samples/3D/materials/materials.epj [new file with mode: 0644]
samples/3D/materials/normal.jpg [new file with mode: 0644]
samples/3D/materials/teapot.3DS [new file with mode: 0644]

diff --git a/samples/3D/materials/materials.ec b/samples/3D/materials/materials.ec
new file mode 100644 (file)
index 0000000..8871f22
--- /dev/null
@@ -0,0 +1,486 @@
+#ifdef ECERE_STATIC
+import static "ecere"
+#else
+import "ecere"
+#endif
+
+class MyApp : GuiApplication
+{
+   driver = "OpenGL";
+   // driver = "Direct3D";
+   timerResolution = 60;
+
+   bool Cycle(bool idle)
+   {
+      materialsTest.updateModel();
+      return true;
+   }
+};
+
+Camera camera
+{
+   attached,
+   position = Vector3D { 0, 0, -300 },
+   orientation = Euler { }; //30, 30, 0 },
+   fovDirection = vertical,
+   fov = 53;
+};
+
+Object cameraTarget { };
+
+Object posLight
+{
+   transform = { position = { 0, 0, -10 }, scaling = { 1, 1, 1 } };
+   light =
+   {
+      multiplier = 1.0f;
+      diffuse = white;
+      Kc = 1.0;
+      //Kc = 0.8;
+      Kl = 0.00002;
+      Kq = 0.000002;
+      specular = white; //blue;
+      //flags = { attenuation = true };
+      lightObject = posLight;
+   };
+};
+
+Light light
+{
+   specular = white;
+   multiplier = 1.0f;
+   diffuse = white;
+   orientation = Euler { pitch = 40, yaw = 50 };
+};
+
+
+define rGlass = 1.52;
+define rWater = 1.33;
+define rIce = 1.309;
+define rDiamond = 2.42;
+
+class MaterialsTest : Window
+{
+   caption = "Materials Test";
+   background = black;
+#if defined(__EMSCRIPTEN__)
+   anchor = { 0, 0, 0, 0 };
+#else
+   borderStyle = sizable;
+   hasMaximize = true;
+   hasMinimize = true;
+   hasClose = true;
+   size = { 640, 480 };
+   state = maximized;
+#endif
+   // glCapabilities.shaders = true;
+
+   Cube cube { };
+   Sphere sphere { };
+   Sphere roundedCylinder { flattenedBody = 0.3f };
+   Object teapot { };
+
+   Object model;
+
+   Euler spin { };
+
+   SkyBox waterBox
+   {
+      size = { 10000, 10000, 10000 },
+      folder = ":watersky", extension = "jpg",
+      cubeMap = waterCubeMap
+   };
+
+   /*
+   SkyBox forestBox
+   {
+      size = { 10000, 10000, 10000 },
+      folder = ":forest", extension = "jpg", newStyle = true,
+      cubeMap = forestCubeMap
+   };
+   */
+   SkyBox sky;
+   font = { "Tahoma", 12, true, outlineSize = 3.0, outlineFade = 0.2 };
+
+   bool updateModel()
+   {
+      static Time lastTime = 0;
+      Time time = GetTime(), diffTime = lastTime ? (time - lastTime) : 0;
+      Transform transform = model.transform;
+      if(spin.yaw || spin.pitch)
+      {
+         int signYaw = 1, signPitch = 1;
+         Radians yaw = spin.yaw, pitch = spin.pitch;
+         Quaternion orientation = transform.orientation;
+         Euler tSpin { yaw * (double)diffTime, pitch * (double)diffTime, 0 };
+         Quaternion thisSpin = tSpin, temp;
+
+         if(yaw < 0) { yaw = -yaw; signYaw = -1; }
+         if(pitch < 0) { pitch = -pitch; signPitch = -1; }
+         yaw   -= (double)diffTime / 3 * yaw;
+         pitch -= (double)diffTime / 3 * pitch;
+         if(yaw < 0.0001) yaw = 0;
+         if(pitch < 0.0001) pitch = 0;
+
+         spin.yaw = yaw * signYaw;
+         spin.pitch = pitch * signPitch;
+
+         temp.Multiply(orientation, thisSpin);
+         orientation.Normalize(temp);
+
+         model.transform.orientation = orientation;
+         //transform.orientation = orientation;
+         //model.transform = transform;
+         model.UpdateTransform();
+         Update(null);
+      }
+      lastTime = time;
+      return true;
+   }
+
+   /*
+   BitmapResource bumpMap { "bumpmap.png", window = this };
+   BitmapResource baseMap { "basetex.bmp", window = this };
+   */
+   BitmapResource bumpMap { ":normal.jpg", mipMaps = true, window = this };
+   // BitmapResource bumpMap { ":normal.png", mipMaps = true, window = this };
+   //BitmapResource baseMap { "diffuse.png", mipMaps = true, window = this };
+   // BitmapResource specularMap { "specular.jpg", mipMaps = true, window = this };
+   CubeMap waterCubeMap { };
+   //CubeMap forestCubeMap { };
+
+   Material material
+   {
+      ambient = blue; //white;
+      diffuse = blue; //white;
+      //specular = { 3.0, 3.0, 3.0 };
+      specular = { 1.0, 1.0, 3.0 };
+      power = 60.0;
+      flags =
+      {
+         doubleSided = true;
+         tile = true;
+         separateSpecular = true;
+      };
+      uScale = 4, vScale = 4;
+      reflectivity = 0.3;
+      refractiveIndex = rGlass;
+      opacity = 0.3;
+   };
+
+   void prepareModel(Object model, float s)
+   {
+      model.transform.scaling = { s, s, s };
+      model.transform.position = { 0, 0, 0 };
+      model.UpdateTransform();
+      model.SetMinMaxRadius(false);
+      model.mesh.ApplyMaterial(material);
+   }
+
+   void viewModel(Object model, double distance, float uvScale)
+   {
+      double r = model.radius * model.transform.scaling.z; //wradius;
+      material.uScale = uvScale;
+      material.vScale = uvScale;
+      camera.position = { 0, 0, -r * distance };
+      cameraTarget.transform.position =
+      {
+         (model.max.x + model.min.x) / 2,
+         (model.max.y + model.min.y) / 2,
+         (model.max.z + model.min.z) / 2
+      };
+      this.model = model;
+      Update(null);
+   }
+
+   void selectEnv(SkyBox sky, CubeMap cubeMap)
+   {
+      material.envMap = cubeMap;
+      this.sky = sky;
+      Update(null);
+   }
+
+   bool OnLoadGraphics()
+   {
+      const String faceNames1[] = { ":watersky/rt", ":watersky/lf",  ":watersky/up", ":watersky/dn", ":watersky/bk", ":watersky/fr" };
+      //const String faceNames2[] = { ":forest/rt", ":forest/lf",  ":forest/up", ":forest/dn", ":forest/bk", ":forest/fr" };
+
+      //material.bumpMap = bumpMap.bitmap;
+      //material.baseMap = baseMap.bitmap;
+      //material.specularMap = specularMap.bitmap;
+      //material.reflectMap = specularMap.bitmap;
+
+      // Water & Sky
+      if(!waterCubeMap.Load(displaySystem, faceNames1, "jpg", true)) waterBox.cubeMap = null;
+      // Forest Cube
+      //if(!forestCubeMap.Load(displaySystem, faceNames2, "jpg", false)) forestBox.cubeMap = null;
+
+      teapot.Load(":teapot.3DS", null, displaySystem);
+      teapot.Merge(displaySystem);
+      teapot.mesh.ApplyMaterial(material);
+      prepareModel(teapot, 1);
+
+      roundedCylinder.Create(displaySystem);
+      prepareModel(roundedCylinder, 50);
+
+      sphere.Create(displaySystem);
+      prepareModel(sphere, 50);
+
+      cube.Create(displaySystem);
+      prepareModel(cube, 50);
+
+      camera.target = cameraTarget;
+      camera.zMin = 1;
+      camera.zMax = 10000;
+
+      waterBox.size = { camera.zMax, camera.zMax, camera.zMax };
+      //forestBox.size = { camera.zMax, camera.zMax, camera.zMax };
+      waterBox.Create(displaySystem);
+      //forestBox.Create(displaySystem);
+
+      viewModel(sphere, 1.5 * sqrt(2), 4.0);
+      //viewModel(cube, 1.5 * 2, 2);
+      //viewModel(teapot, 1.5 * 2, 2);
+      selectEnv(waterBox, waterCubeMap);
+      // selectEnv(forestBox, forestCubeMap);
+      return true;
+   }
+
+   void OnResize(int w, int h)
+   {
+      camera.Setup(w, h, null);
+      camera.Update();
+   }
+
+   void OnRedraw(Surface surface)
+   {
+      int x, y;
+
+      camera.Update();
+      GetMousePosition(&x, &y);
+
+      posLight.transform.position =
+      {
+         (x - clientSize.w / 2) * 400 / clientSize.w,
+         (y - clientSize.h / 2) * 400 / clientSize.h,
+         - 2 * model.wradius
+      };
+      posLight.UpdateTransform();
+
+      surface.Clear(depthBuffer);
+
+      // Setting the light before the camera means the light is in view space
+      //display.SetLight(0, posLight.light);
+      display.SetLight(0, light);
+      // display.ambient = { };
+
+      //display.ambient = { 255, 0, 0 };
+      display.SetCamera(surface, camera);
+
+      // Setting the light before the camera means the light is in world space
+      //display.SetLight(0, light);
+
+      sky.Render(camera, display);
+
+      display.DrawObject(model);
+      display.SetCamera(surface, null);
+
+      surface.outlineColor = black;
+      surface.foreground = white;
+
+      surface.WriteTextf(10, 10,  "('R' to toggle')   Reflection:     %s,   Refraction: %s",
+         material.reflectivity ? "ON" : "OFF",
+         material.refractiveIndex ? "ON" : "OFF");
+      surface.WriteTextf(10, 40,  "('C' to toggle')   Shape:            %s ",
+         model == cube ? "Cube" : model == sphere ? "Sphere" : model == roundedCylinder ? "Rounded Cylinder" : "Teapot");
+      /*surface.WriteTextf(10, 70,  "('E' to toggle')   Environment:  %s",
+         material.envMap == waterCubeMap ? "Water & Sky" : "Forest");*/
+      surface.WriteTextf(10, 100, "('B' to toggle')   Normal Map:   %s",
+         material.bumpMap ? "ON" : "OFF");
+      surface.WriteTextf(10, 130, "Left-Button Drag: Rotate Camera;   Middle-Button Drag: Spin Model;   Right-Button Drag: Rotate Light");
+      surface.WriteTextf(10, 160, "Scroll Wheel: Move camera closer / further");
+#ifndef __EMSCRIPTEN__
+      surface.WriteTextf(10, 200, "Shaders:     %s", glCapabilities.shaders ? "ON" : "OFF");
+#endif
+   }
+
+   Point startClick;
+   bool moving, movingCamera, movingLight;
+   Time clickTime;
+   Point startPosition;
+   Euler startOrientation;
+
+   bool OnMiddleButtonDown(int x, int y, Modifiers mods)
+   {
+      clickTime = GetTime();
+      Capture();
+      startClick = { x, y };
+      moving = true;
+      return true;
+   }
+
+   bool OnMiddleButtonUp(int x, int y, Modifiers mods)
+   {
+      if(moving)
+      {
+         ReleaseCapture();
+         moving = false;
+      }
+      return true;
+   }
+
+   bool OnLeftButtonDown(int x, int y, Modifiers mods)
+   {
+      if(!movingCamera && !moving && !movingLight)
+      {
+         startPosition.x = x;
+         startPosition.y = y;
+         startOrientation = camera.eulerOrientation;
+         Capture();
+         movingCamera = true;
+      }
+      return true;
+   }
+
+   bool OnLeftButtonUp(int x, int y, Modifiers mods)
+   {
+      if(movingCamera)
+      {
+         ReleaseCapture();
+         movingCamera = false;
+      }
+      return true;
+   }
+
+   bool OnRightButtonDown(int x, int y, Modifiers mods)
+   {
+      if(!moving && !movingLight)
+      {
+         startPosition.x = x;
+         startPosition.y = y;
+         startOrientation = light.orientation;
+         Capture();
+         movingLight = true;
+      }
+      return true;
+   }
+
+   bool OnRightButtonUp(int x, int y, Modifiers mods)
+   {
+      if(movingLight)
+      {
+         ReleaseCapture();
+         movingLight = false;
+      }
+      return true;
+   }
+
+   bool OnMouseMove(int x, int y, Modifiers mods)
+   {
+      if(moving)
+      {
+         Time time = GetTime(), diffTime = Max(time - clickTime, 0.01);
+         spin.yaw   += Degrees { (x - startClick.x) / (25.0 * (double)diffTime) };
+         spin.pitch += Degrees { (startClick.y - y) / (25.0 * (double)diffTime) };
+         startClick = { x, y };
+         clickTime = time;
+      }
+      else if(movingCamera)
+      {
+         Euler euler
+         {
+            startOrientation.yaw   - (x - startPosition.x),
+            startOrientation.pitch + (y - startPosition.y),
+            0
+         };
+
+         camera.eulerOrientation = euler;
+
+         Update(null);
+      }
+      else if(movingLight)
+      {
+         light.orientation = Euler
+         {
+            startOrientation.yaw   - (x - startPosition.x),
+            startOrientation.pitch + (y - startPosition.y),
+            0
+         };
+
+         Update(null);
+      }
+      Update(null);
+      return true;
+   }
+
+   bool OnKeyHit(Key key, unichar ch)
+   {
+      switch(key)
+      {
+         case wheelDown: case minus: camera.position.z *= 1.1111111f; Update(null); break;
+         case wheelUp: case plus: camera.position.z *= 0.9f; Update(null); break;
+         case f3:
+            glCapabilities.shaders ^= true;
+            Update(null);
+            break;
+         case c:
+            if(model == cube)
+               viewModel(sphere, 1.5 * sqrt(2), 4);
+            else if(model == sphere)
+               viewModel(roundedCylinder, 1.5 * sqrt(2), 4);
+            else if(model == roundedCylinder)
+               viewModel(teapot, 1.5 * sqrt(2), 1.5);
+            else if(model == teapot)
+               viewModel(cube, 1.5 * 2, 2);
+            Update(null);
+            break;
+         /*case e:
+            if(sky == waterBox)
+               selectEnv(forestBox, forestCubeMap);
+            else
+               selectEnv(waterBox, waterCubeMap);
+            break;*/
+         case d:
+         {
+            static bool debugging = false;
+            debugging ^= true;
+            DefaultShader::shader().debugging(debugging);
+            Update(null);
+            break;
+         }
+         case b:
+            material.bumpMap = material.bumpMap ? null : bumpMap.bitmap;
+            Update(null);
+            break;
+         case r:
+            if(material.reflectivity && material.refractiveIndex)
+            {
+               material.refractiveIndex = 0;
+               material.opacity = 1.0;
+               material.reflectivity = 0.8;
+            }
+            else if(material.reflectivity)
+            {
+               material.reflectivity = 0;
+               material.refractiveIndex = rGlass;
+               material.opacity = 0.3;
+            }
+            else if(material.refractiveIndex)
+            {
+               material.refractiveIndex = 0.0;
+               material.opacity = 1.0;
+            }
+            else
+            {
+               material.reflectivity = 0.3;
+               material.refractiveIndex = rGlass;
+               material.opacity = 0.3;
+            }
+            Update(null);
+            break;
+      }
+      return true;
+   }
+}
+
+MaterialsTest materialsTest {};
diff --git a/samples/3D/materials/materials.epj b/samples/3D/materials/materials.epj
new file mode 100644 (file)
index 0000000..900cbdc
--- /dev/null
@@ -0,0 +1,112 @@
+{
+   "Version" : 0.2,
+   "ModuleName" : "materials",
+   "Options" : {
+      "Warnings" : "All",
+      "TargetType" : "Executable",
+      "TargetFileName" : "materials",
+      "Libraries" : [
+         "ecere"
+      ]
+   },
+   "Configurations" : [
+      {
+         "Name" : "Debug",
+         "Options" : {
+            "Debug" : true,
+            "Optimization" : "None",
+            "PreprocessorDefinitions" : [
+               "_DEBUG"
+            ],
+            "Console" : true,
+            "FastMath" : false
+         }
+      },
+      {
+         "Name" : "Release",
+         "Options" : {
+            "Debug" : false,
+            "Optimization" : "Speed",
+            "FastMath" : true
+         }
+      },
+      {
+         "Name" : "Emscripten",
+         "Options" : {
+            "Optimization" : "Speed",
+            "PreprocessorDefinitions" : [
+               "ECERE_STATIC"
+            ],
+            "TargetFileName" : "materials.html",
+            "Libraries" : [
+               "ecereStatic",
+               "z",
+               "freetype",
+               "jpeg",
+               "png"
+            ],
+            "LibraryDirs" : [
+               "../../../ecere/obj/emscripten.linux.emscripten"
+            ],
+            "FastMath" : true
+         }
+      }
+   ],
+   "Files" : [
+      "materials.ec"
+   ],
+   "ResourcesPath" : "",
+   "Resources" : [
+      {
+         "Folder" : "watersky",
+         "Files" : [
+            "../ModelViewer/skycube/bk.jpg",
+            "../ModelViewer/skycube/dn.jpg",
+            "../ModelViewer/skycube/fr.jpg",
+            "../ModelViewer/skycube/lf.jpg",
+            "../ModelViewer/skycube/rt.jpg",
+            "../ModelViewer/skycube/up.jpg"
+         ]
+      },
+      {
+         "Folder" : "forest",
+         "Files" : [
+            "bk.jpg",
+            "dn.jpg",
+            "fr.jpg",
+            "lf.jpg",
+            "rt.jpg",
+            "up.jpg"
+         ],
+         "Options" : {
+            "ExcludeFromBuild" : true
+         }
+      },
+      {
+         "Folder" : "ecere",
+         "Files" : [
+            {
+               "Folder" : "shaders",
+               "Files" : [
+                  "../../../ecere/src/gfx/drivers/gl3/default.frag",
+                  "../../../ecere/src/gfx/drivers/gl3/default.vert"
+               ]
+            },
+            "C:/Windows/Fonts/tahomabd.ttf"
+         ],
+         "Options" : {
+            "ExcludeFromBuild" : true
+         },
+         "Configurations" : [
+            {
+               "Name" : "Emscripten",
+               "Options" : {
+                  "ExcludeFromBuild" : false
+               }
+            }
+         ]
+      },
+      "teapot.3DS",
+      "normal.jpg"
+   ]
+}
diff --git a/samples/3D/materials/normal.jpg b/samples/3D/materials/normal.jpg
new file mode 100644 (file)
index 0000000..f175dc8
Binary files /dev/null and b/samples/3D/materials/normal.jpg differ
diff --git a/samples/3D/materials/teapot.3DS b/samples/3D/materials/teapot.3DS
new file mode 100644 (file)
index 0000000..c48f27e
Binary files /dev/null and b/samples/3D/materials/teapot.3DS differ