Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 // | 4 // |
| 5 // Canvas API tests. Some of these are adapted from: | 5 // Canvas API tests. Some of these are adapted from: |
| 6 // | 6 // |
| 7 // http://www.html5canvastutorials.com/ | 7 // http://www.html5canvastutorials.com/ |
| 8 | 8 |
| 9 library openglui_canvas_tests; | 9 library openglui_canvas_tests; |
| 10 | 10 |
| 11 import 'gl.dart'; | 11 import 'gl.dart'; |
| 12 import 'dart:math' as Math; | 12 import 'dart:math' as Math; |
| 13 | 13 |
| 14 var ctx; | 14 var ctx; |
| 15 var width, height; | 15 var width, height; |
| 16 bool isDirty = true; | 16 bool isDirty = true; |
| 17 var canvas; | 17 var canvas; |
| 18 | 18 |
| 19 void resize(int w, int h) { | 19 void resize(int w, int h) { |
| 20 width = w; | 20 width = w; |
| 21 height = h; | 21 height = h; |
| 22 } | 22 } |
| 23 | 23 |
| 24 void setup(canvasp, int w, int h) { | 24 void setup(canvasp, int w, int h) { |
| 25 if (canvasp == null) { | 25 if (canvasp == null) { |
| 26 log("Allocating canvas"); | 26 log("Allocating canvas"); |
| 27 canvas = new CanvasElement(width: w, height: h); | 27 canvas = new CanvasElement(width: w, height: h); |
| 28 document.body.nodes.add(canvas); | |
| 28 } else { | 29 } else { |
| 29 log("Using parent canvas"); | 30 log("Using parent canvas"); |
| 30 canvas = canvasp; | 31 canvas = canvasp; |
| 31 // called from gl_driver.dart. | |
| 32 // This is a kludge; we need a clean way of handling events. | |
| 33 canvas.on.mouseDown.add((e) { | |
| 34 ++testnum; | |
| 35 }); | |
| 36 } | 32 } |
| 33 canvas.onMouseDown.listen((e) { | |
| 34 ++testnum; | |
| 35 isDirty = true; | |
| 36 }); | |
| 37 canvas.onKeyDown.listen((e) { | |
| 38 ++testnum; | |
| 39 isDirty = true; | |
| 40 }); | |
| 37 ctx = canvas.getContext("2d"); | 41 ctx = canvas.getContext("2d"); |
| 38 if (ctx == null) { | 42 if (ctx == null) { |
| 39 throw "Failed to get 2D context"; | 43 throw "Failed to get 2D context"; |
| 40 } | 44 } |
| 41 resize(w, h); | 45 resize(w, h); |
| 42 log("Done setup"); | 46 log("Done setup"); |
| 43 } | 47 } |
| 44 | 48 |
| 45 resetCanvas() { | 49 resetCanvas() { |
| 46 ctx.globalCompositeOperation = "source-over"; | 50 ctx.globalCompositeOperation = "source-over"; |
| (...skipping 595 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 642 | 646 |
| 643 ctx.beginPath(); | 647 ctx.beginPath(); |
| 644 ctx.rect(myRectangle.x, myRectangle.y, myRectangle.width, myRectangle.height); | 648 ctx.rect(myRectangle.x, myRectangle.y, myRectangle.width, myRectangle.height); |
| 645 ctx.fillStyle = '#8ED6FF'; | 649 ctx.fillStyle = '#8ED6FF'; |
| 646 ctx.fill(); | 650 ctx.fill(); |
| 647 ctx.lineWidth = myRectangle.borderWidth; | 651 ctx.lineWidth = myRectangle.borderWidth; |
| 648 ctx.strokeStyle = 'black'; | 652 ctx.strokeStyle = 'black'; |
| 649 ctx.stroke(); | 653 ctx.stroke(); |
| 650 } | 654 } |
| 651 | 655 |
| 652 int testnum = -1; // Start with latest. | 656 int testnum = 0; //-1; // Start with latest. |
|
vsm
2013/03/05 00:23:58
Did you mean to keep this change? If so, delete t
gram
2013/03/05 01:06:49
Done.
| |
| 653 | 657 |
| 654 void update() { | 658 void update() { |
| 655 if (testnum == 0) { | 659 if (testnum == 0) { |
| 656 anim(); | 660 anim(); |
| 657 return; | 661 return; |
| 658 } | 662 } |
| 659 if (!isDirty) return; | 663 if (!isDirty) return; |
| 660 switch(testnum) { | 664 switch(testnum) { |
| 661 case 1: | 665 case 1: |
| 662 helloWorld(); | 666 helloWorld(); |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 717 break; | 721 break; |
| 718 case 20: | 722 case 20: |
| 719 shear(); | 723 shear(); |
| 720 break; | 724 break; |
| 721 case 21: | 725 case 21: |
| 722 composite(); | 726 composite(); |
| 723 break; | 727 break; |
| 724 case 22: | 728 case 22: |
| 725 smiley(); | 729 smiley(); |
| 726 break; | 730 break; |
| 731 case 23: | |
| 732 var rayTracer = new RayTracer(); | |
| 733 rayTracer.render(defaultScene(), ctx, width, height); | |
| 734 break; | |
| 727 default: | 735 default: |
| 728 if (testnum < 0) { | 736 if (testnum < 0) { |
| 729 testnum = 22; | 737 testnum = 23; |
| 730 } else { | 738 } else { |
| 731 testnum = 0; | 739 testnum = 0; |
| 732 } | 740 } |
| 733 update(); | 741 update(); |
| 734 break; | 742 break; |
| 735 } | 743 } |
| 736 isDirty = false; | 744 isDirty = false; |
| 737 } | 745 } |
| 738 | 746 |
| 739 /* | 747 // Raytracer adapted from https://gist.github.com/mythz/3817303. |
| 740 TODO(gram): below are the current entry points for input events for the | 748 |
| 741 native code version. We need to integrate these somehow with the WebGL | 749 Scene defaultScene() => |
| 742 version: | 750 new Scene( |
| 743 */ | 751 [new Plane(new Vector(0.0, 1.0, 0.0), 0.0, Surfaces.checkerboard), |
| 744 onMotionDown(num when, num x, num y) { | 752 new Sphere(new Vector(0.0, 1.0, -0.25), 1.0, Surfaces.shiny), |
| 745 ++testnum; | 753 new Sphere(new Vector(-1.0, 0.5, 1.5), 0.5, Surfaces.shiny)], |
| 746 isDirty = true; | 754 [new Light(new Vector(-2.0, 2.5, 0.0), new Color(0.49, 0.07, 0.07) ), |
| 747 log("Marking dirty"); | 755 new Light(new Vector(1.5, 2.5, 1.5), new Color(0.07, 0.07, 0.49) ), |
| 748 } | 756 new Light(new Vector(1.5, 2.5, -1.5), new Color(0.07, 0.49, 0.071) ), |
| 749 onMotionUp(num when, num x, num y) {} | 757 new Light(new Vector(0.0, 3.5, 0.0), new Color(0.21, 0.21, 0.35) )], |
| 750 onMotionMove(num when, num x, num y) {} | 758 new Camera(new Vector(3.0, 2.0, 4.0), new Vector(-1.0, 0.5, 0.0)) |
| 751 onMotionCancel(num when, num x, num y) {} | 759 ); |
| 752 onMotionOutside(num when, num x, num y) {} | 760 |
| 753 onMotionPointerDown(num when, num x, num y) { | 761 |
| 754 ++testnum; | 762 class Vector { |
| 755 isDirty = true; | 763 num x, y, z; |
| 756 log("Marking dirty"); | 764 Vector(this.x, this.y, this.z); |
| 757 } | 765 |
| 758 | 766 operator -(Vector v) => new Vector(x - v.x, y - v.y, z - v.z); |
| 759 onMotionPointerUp(num when, num x, num y) {} | 767 operator +(Vector v) => new Vector(x + v.x, y + v.y, z + v.z); |
| 760 | 768 static times(num k, Vector v) => new Vector(k * v.x, k * v.y, k * v.z); |
| 761 onKeyDown(num when, int flags, int keycode, int metastate, int repeat) { | 769 static num dot(Vector v1, Vector v2) => v1.x * v2.x + v1.y * v2.y + v1.z * v 2.z; |
|
vsm
2013/03/05 00:23:58
line len
vsm
2013/03/05 00:23:58
len
gram
2013/03/05 01:06:49
Done.
gram
2013/03/05 01:06:49
Done.
| |
| 762 if (keycode == 32) { | 770 static num mag(Vector v) => Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z); |
| 763 ++testnum; | 771 static Vector norm(Vector v) { |
| 764 isDirty = true; | 772 var _mag = mag(v); |
| 765 log("Marking dirty"); | 773 var div = _mag == 0 ? double.INFINITY : 1.0 / _mag; |
| 774 return times(div, v); | |
| 775 } | |
| 776 static Vector cross(Vector v1, Vector v2) { | |
| 777 return new Vector(v1.y * v2.z - v1.z * v2.y, | |
| 778 v1.z * v2.x - v1.x * v2.z, | |
| 779 v1.x * v2.y - v1.y * v2.x); | |
| 780 } | |
| 781 } | |
| 782 | |
| 783 class Color { | |
| 784 num r, g, b; | |
| 785 static final white = new Color(1.0, 1.0, 1.0); | |
| 786 static final grey = new Color(0.5, 0.5, 0.5); | |
| 787 static final black = new Color(0.0, 0.0, 0.0); | |
| 788 static final background = Color.black; | |
| 789 static final defaultColor = Color.black; | |
| 790 | |
| 791 Color(this.r,this.g,this.b); | |
| 792 static scale(num k, Color v) => new Color(k * v.r, k * v.g, k * v.b); | |
| 793 operator +(Color v) => new Color(r + v.r, g + v.g, b + v.b); | |
| 794 operator *(Color v) => new Color(r * v.r, g * v.g, b * v.b); | |
| 795 static _intColor(num d) => ((d > 1 ? 1 : d) * 255).toInt(); | |
| 796 static String toDrawingRGB(Color c) =>"rgb(${_intColor(c.r)}, ${_intColor(c.g )}, ${_intColor(c.b)})"; | |
|
vsm
2013/03/05 00:23:58
line
gram
2013/03/05 01:06:49
Done.
| |
| 797 } | |
| 798 | |
| 799 class Camera { | |
| 800 Vector pos, forward, right, up; | |
| 801 Camera (this.pos, Vector lookAt) { | |
| 802 var down = new Vector(0.0, -1.0, 0.0); | |
| 803 forward = Vector.norm(lookAt - pos); | |
| 804 right = Vector.times(1.5, Vector.norm(Vector.cross(forward, down))); | |
| 805 up = Vector.times(1.5, Vector.norm(Vector.cross(forward, right))); | |
| 766 } | 806 } |
| 767 } | 807 } |
| 768 | 808 |
| 769 onKeyUp(num when, int flags, int keycode, int metastate, int repeat) {} | 809 class Ray { |
| 770 | 810 Vector start, dir; |
| 771 onKeyMultiple(num when, int flags, int keycode, int metastate, int repeat) { | 811 Ray([this.start, this.dir]); |
| 772 } | 812 } |
| 773 | 813 |
| 774 | 814 class Intersection { |
| 815 Thing thing; | |
| 816 Ray ray; | |
| 817 num dist; | |
| 818 Intersection(this.thing, this.ray, this.dist); | |
| 819 } | |
| 820 | |
| 821 class Light { | |
| 822 Vector pos; | |
| 823 Color color; | |
| 824 Light(this.pos, this.color); | |
| 825 } | |
| 826 | |
| 827 abstract class Surface { | |
| 828 int roughness; | |
| 829 Color diffuse(Vector pos); | |
| 830 Color specular(Vector pos); | |
| 831 num reflect(Vector pos); | |
| 832 } | |
| 833 | |
| 834 abstract class Thing { | |
| 835 Intersection intersect(Ray ray); | |
| 836 Vector normal(Vector pos); | |
| 837 Surface surface; | |
| 838 } | |
| 839 | |
| 840 class Scene { | |
| 841 List<Thing> things; | |
| 842 List<Light> lights; | |
| 843 Camera camera; | |
| 844 Scene([this.things,this.lights,this.camera]); | |
| 845 } | |
| 846 | |
| 847 class Sphere implements Thing { | |
| 848 num radius2, radius; | |
| 849 Vector center; | |
| 850 Surface surface; | |
| 851 | |
| 852 Sphere (this.center, this.radius, this.surface) { | |
| 853 this.radius2 = radius * radius; | |
| 854 } | |
| 855 normal(Vector pos) => Vector.norm(pos - center); | |
| 856 intersect(Ray ray) { | |
| 857 var eo = this.center - ray.start; | |
| 858 var v = Vector.dot(eo, ray.dir); | |
| 859 var dist = 0; | |
| 860 if (v >= 0) { | |
| 861 var disc = this.radius2 - (Vector.dot(eo, eo) - v * v); | |
| 862 if (disc >= 0) | |
| 863 dist = v - Math.sqrt(disc); | |
| 864 } | |
| 865 return dist == 0 ? null : new Intersection(this, ray, dist); | |
| 866 } | |
| 867 } | |
| 868 | |
| 869 class Plane implements Thing { | |
| 870 Vector norm; | |
| 871 num offset; | |
| 872 Surface surface; | |
| 873 Plane(this.norm, this.offset, this.surface); | |
| 874 Vector normal(Vector pos) => norm; | |
| 875 Intersection intersect(Ray ray) { | |
| 876 var denom = Vector.dot(norm, ray.dir); | |
| 877 if (denom > 0) { | |
| 878 return null; | |
| 879 } else { | |
| 880 var dist = (Vector.dot(norm, ray.start) + offset) / (-denom); | |
| 881 return new Intersection(this, ray, dist); | |
| 882 } | |
| 883 } | |
| 884 } | |
| 885 | |
| 886 class CustomSurface implements Surface { | |
| 887 Color diffuseColor, specularColor; | |
| 888 int roughness; | |
| 889 num reflectPos; | |
| 890 CustomSurface(this.diffuseColor, this.specularColor, this.reflectPos, this.rou ghness); | |
|
vsm
2013/03/05 00:23:58
length
gram
2013/03/05 01:06:49
Done.
| |
| 891 diffuse(pos) => diffuseColor; | |
| 892 specular(pos) => specularColor; | |
| 893 reflect(pos) => reflectPos; | |
| 894 } | |
| 895 | |
| 896 class CheckerBoardSurface implements Surface { | |
| 897 int roughness; | |
| 898 CheckerBoardSurface([this.roughness=150]); | |
| 899 diffuse(pos) => (pos.z.floor() + pos.x.floor()) % 2 != 0 | |
| 900 ? Color.white | |
| 901 : Color.black; | |
| 902 specular(pos) => Color.white; | |
| 903 reflect(pos) => (pos.z.floor() + pos.x.floor()) % 2 != 0 ? 0.1 : 0.7; | |
| 904 } | |
| 905 | |
| 906 class Surfaces { | |
| 907 static final shiny = new CustomSurface(Color.white, Color.grey, 0.7, 250); | |
| 908 static final checkerboard = new CheckerBoardSurface(); | |
| 909 } | |
| 910 | |
| 911 class RayTracer { | |
| 912 num _maxDepth = 5; | |
| 913 | |
| 914 Intersection _intersections(Ray ray, Scene scene) { | |
| 915 var closest = double.INFINITY; | |
| 916 Intersection closestInter = null; | |
| 917 for (Thing thing in scene.things) { | |
| 918 var inter = thing.intersect(ray); | |
| 919 if (inter != null && inter.dist < closest) { | |
| 920 closestInter = inter; | |
| 921 closest = inter.dist; | |
| 922 } | |
| 923 } | |
| 924 return closestInter; | |
| 925 } | |
| 926 | |
| 927 _testRay(Ray ray, Scene scene) { | |
| 928 var isect = _intersections(ray, scene); | |
| 929 return isect != null | |
| 930 ? isect.dist | |
| 931 : null; | |
| 932 } | |
| 933 | |
| 934 _traceRay(Ray ray, Scene scene, num depth) { | |
| 935 var isect = _intersections(ray, scene); | |
| 936 return isect == null | |
| 937 ? Color.background | |
| 938 : _shade(isect, scene, depth); | |
| 939 } | |
| 940 | |
| 941 _shade(Intersection isect, Scene scene, num depth) { | |
| 942 var d = isect.ray.dir; | |
| 943 var pos = Vector.times(isect.dist, d) + isect.ray.start; | |
| 944 var normal = isect.thing.normal(pos); | |
|
vsm
2013/03/05 00:23:58
Ditto line length for the next few methods.
gram
2013/03/05 01:06:49
Done.
| |
| 945 var reflectDir = d - Vector.times(2, Vector.times(Vector.dot(normal, d), normal)); | |
| 946 var naturalColor = Color.background + _getNaturalColor(isect.thing, pos, normal, reflectDir, scene); | |
| 947 var reflectedColor = (depth >= _maxDepth) ? Color.grey : _getReflectionCo lor(isect.thing, pos, normal, reflectDir, scene, depth); | |
| 948 return naturalColor + reflectedColor; | |
| 949 } | |
| 950 | |
| 951 _getReflectionColor(Thing thing, Vector pos, Vector normal, Vector rd, Scene scene, num depth) => | |
| 952 Color.scale(thing.surface.reflect(pos), _traceRay(new Ray(pos, rd), scene , depth + 1)); | |
| 953 | |
| 954 _getNaturalColor(Thing thing, Vector pos, Vector norm, Vector rd, Scene scene ) { | |
| 955 var addLight = (col, light) { | |
| 956 var ldis = light.pos - pos; | |
| 957 var livec = Vector.norm(ldis); | |
| 958 var neatIsect = _testRay(new Ray(pos, livec), scene); | |
| 959 var isInShadow = neatIsect == null ? false : (neatIsect <= Vector.mag (ldis)); | |
| 960 if (isInShadow) { | |
| 961 return col; | |
| 962 } else { | |
| 963 var illum = Vector.dot(livec, norm); | |
| 964 var lcolor = (illum > 0) ? Color.scale(illum, light.color) | |
| 965 : Color.defaultColor; | |
| 966 var specular = Vector.dot(livec, Vector.norm(rd)); | |
| 967 var scolor = (specular > 0) ? Color.scale(Math.pow(specular, thin g.surface.roughness), light.color) | |
| 968 : Color.defaultColor; | |
| 969 return col + (thing.surface.diffuse(pos) * lcolor) | |
| 970 + (thing.surface.specular(pos) * scolor); | |
| 971 } | |
| 972 }; | |
| 973 return scene.lights.reduce(Color.defaultColor, addLight); | |
| 974 } | |
| 975 | |
| 976 render(Scene scene, CanvasRenderingContext2D ctx, num screenWidth, num screen Height) { | |
| 977 var getPoint = (x, y, camera) { | |
| 978 var recenterX = (x) => (x - (screenWidth / 2.0)) / 2.0 / screenWidth; | |
| 979 var recenterY = (y) => - (y - (screenHeight / 2.0)) / 2.0 / screenHei ght; | |
| 980 return Vector.norm(camera.forward | |
| 981 + Vector.times(recenterX(x), camera.right) | |
| 982 + Vector.times(recenterY(y), camera.up)); | |
| 983 }; | |
| 984 for (int y = 0; y < screenHeight; y++) { | |
| 985 for (int x = 0; x < screenWidth; x++) { | |
| 986 var color = _traceRay(new Ray(scene.camera.pos, getPoint(x, y, sc ene.camera) ), scene, 0); | |
| 987 ctx.fillStyle = Color.toDrawingRGB(color); | |
| 988 ctx.fillRect(x, y, x + 1, y + 1); | |
| 989 } | |
| 990 } | |
| 991 } | |
| 992 } | |
| 993 | |
| 994 | |
| 995 | |
| 996 | |
| OLD | NEW |