Browse Source

buffergeometry article

Gregg Tavares 6 years ago
parent
commit
b0d9723e03

+ 51 - 0
threejs/lessons/resources/cube-faces-vertex.svg

@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg width="100%" height="100%" viewBox="0 0 500 420" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
+    <g transform="matrix(1,0,0,1,10,-60)">
+        <g transform="matrix(1,0,0,1,-80,66)">
+            <path d="M100,300L300,394L300,154L100,54L100,300Z" style="fill:rgb(177,255,195);stroke:black;stroke-width:1px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,-40,20)">
+            <path d="M60,100L260,200L500,160L300,60L60,100Z" style="fill:rgb(255,177,195);stroke:black;stroke-width:1px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,-20,0)">
+            <path d="M480,180L240,220L240,460L480,420L480,180Z" style="fill:rgb(177,201,255);stroke:black;stroke-width:1px;"/>
+        </g>
+    </g>
+    <g transform="matrix(6.12323e-17,1,-1,0.161122,423.913,89.0193)">
+        <path d="M253.23,133.913L66.77,133.913L66.77,6.087L98.313,6.087L98.313,96.265L142.448,96.265L142.448,18.424L173.991,18.424L173.991,96.265L253.23,96.265L253.23,133.913Z" style="fill:rgb(157,185,250);fill-rule:nonzero;"/>
+    </g>
+    <g transform="matrix(6.12323e-17,1,-1,-0.498582,193.913,104.765)">
+        <path d="M253.23,133.913L66.77,133.913L66.77,6.087L98.313,6.087L98.313,96.265L142.448,96.265L142.448,18.424L173.991,18.424L173.991,96.265L253.23,96.265L253.23,133.913Z" style="fill:rgb(125,253,155);fill-rule:nonzero;"/>
+    </g>
+    <g transform="matrix(6.12323e-17,1,-1,6.12323e-17,505,-40)">
+        <path d="M180,240L103.149,390L83.149,265L96.797,231.682L113.149,330L123.149,310L112,245L121.935,216.818L133.149,280L173.149,200L180,240Z" style="fill:rgb(255,159,181);"/>
+    </g>
+    <g transform="matrix(1,0,0,1,10,-60)">
+        <circle cx="220.652" cy="222.077" r="7.923"/>
+    </g>
+    <g transform="matrix(1,0,0,1,10,-60)">
+        <g transform="matrix(1.36479,0,0,1,-74.2529,-12.5)">
+            <path d="M216.365,185L220,200L212.729,200L216.365,185Z" style="fill:rgb(175,0,0);"/>
+        </g>
+        <g transform="matrix(1.0146,0,0,1.5,-5.26493,-105)">
+            <rect x="221.039" y="195" width="3.961" height="15" style="fill:rgb(175,0,0);"/>
+        </g>
+    </g>
+    <g transform="matrix(-0.142773,-0.989756,0.989756,-0.142773,42.122,411.717)">
+        <g transform="matrix(1.36479,0,0,1,-74.2529,-12.5)">
+            <path d="M216.365,185L220,200L212.729,200L216.365,185Z" style="fill:rgb(25,175,0);"/>
+        </g>
+        <g transform="matrix(1.0146,0,0,1.5,-5.26493,-105)">
+            <rect x="221.039" y="195" width="3.961" height="15" style="fill:rgb(25,175,0);"/>
+        </g>
+    </g>
+    <g transform="matrix(-0.44533,0.895367,-0.895367,-0.44533,527.886,62.9089)">
+        <g transform="matrix(1.36479,0,0,1,-74.2529,-12.5)">
+            <path d="M216.365,185L220,200L212.729,200L216.365,185Z" style="fill:rgb(0,42,175);"/>
+        </g>
+        <g transform="matrix(1.0146,0,0,1.5,-5.26493,-105)">
+            <rect x="221.039" y="195" width="3.961" height="15" style="fill:rgb(0,42,175);"/>
+        </g>
+    </g>
+</svg>

+ 904 - 0
threejs/lessons/resources/threejs-attributes.svg

@@ -0,0 +1,904 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg width="100%" height="100%" viewBox="0 0 410 140" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
+    <g transform="matrix(0.975,0,0,0.769231,-29,-0.492041)">
+        <rect x="40" y="27" width="400" height="13" style="fill:rgb(0,180,216);stroke:black;stroke-width:0.23px;"/>
+    </g>
+    <g transform="matrix(0.975,0,0,0.769231,-29,29.508)">
+        <rect x="40" y="27" width="400" height="13" style="fill:rgb(0,145,228);stroke:black;stroke-width:0.23px;"/>
+    </g>
+    <g transform="matrix(0.975,0,0,0.769231,-29,59.508)">
+        <rect x="40" y="27" width="400" height="13" style="fill:rgb(0,180,216);stroke:black;stroke-width:0.23px;"/>
+    </g>
+    <g transform="matrix(0.975,0,0,0.769231,-29,89.508)">
+        <rect x="40" y="27" width="400" height="13" style="fill:rgb(8,166,122);stroke:black;stroke-width:0.23px;"/>
+    </g>
+    <g transform="matrix(1,0,0,1,-200.343,-5.94665)">
+        <text x="212.844px" y="33.448px" style="font-family:'Arial-BoldMT', 'Arial', sans-serif;font-weight:700;font-size:9px;">position</text>
+    </g>
+    <g transform="matrix(1,0,0,1,-200.343,24.0534)">
+        <text x="212.844px" y="33.448px" style="font-family:'Arial-BoldMT', 'Arial', sans-serif;font-weight:700;font-size:9px;">normal</text>
+    </g>
+    <g transform="matrix(1,0,0,1,-200.343,54.0534)">
+        <text x="212.844px" y="33.448px" style="font-family:'Arial-BoldMT', 'Arial', sans-serif;font-weight:700;font-size:9px;">color</text>
+    </g>
+    <g transform="matrix(1,0,0,1,-200.343,84.0534)">
+        <text x="212.844px" y="33.448px" style="font-family:'Arial-BoldMT', 'Arial', sans-serif;font-weight:700;font-size:9px;">uv</text>
+    </g>
+    <g transform="matrix(1,0,0,1,-30,-49.7228)">
+        <g transform="matrix(1,0,0,1,20,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,50,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,80,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,110,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,140,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,170,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,200,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,230,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,260,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,290,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,320,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,350,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,380,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,30,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,60,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,90,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,120,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,150,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,180,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,210,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,240,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,270,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,300,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,330,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,360,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,390,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,40,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,70,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,100,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,130,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,160,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,190,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,220,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,250,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,280,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,310,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,340,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,370,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,400,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+    </g>
+    <g transform="matrix(1,0,0,1,-30,-19.7228)">
+        <g transform="matrix(1,0,0,1,20,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,50,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,80,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,110,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,140,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,170,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,200,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,230,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,260,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,290,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,320,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,350,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,380,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,30,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,60,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,90,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,120,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,150,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,180,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,210,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,240,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,270,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,300,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,330,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,360,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,390,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,40,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,70,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,100,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,130,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,160,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,190,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,220,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,250,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,280,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,310,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,340,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,370,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,400,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+    </g>
+    <g transform="matrix(1,0,0,1,-30,10.2772)">
+        <g transform="matrix(1,0,0,1,20,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,50,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,80,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,110,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,140,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,170,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,200,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,230,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,260,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,290,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,320,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,350,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,380,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,30,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,60,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,90,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,120,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,150,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,180,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,210,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,240,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,270,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,300,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,330,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,360,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,390,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,40,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,70,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,100,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,130,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,160,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,190,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,220,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,250,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,280,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,310,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,340,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,370,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,400,40)">
+            <rect x="20" y="40" width="10" height="10" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+    </g>
+    <g>
+        <rect x="10" y="120.277" width="15" height="10" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+        <g transform="matrix(1,0,0,1,30,0)">
+            <rect x="10" y="120.277" width="15" height="10" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,60,0)">
+            <rect x="10" y="120.277" width="15" height="10" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,90,0)">
+            <rect x="10" y="120.277" width="15" height="10" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,120,0)">
+            <rect x="10" y="120.277" width="15" height="10" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,150,0)">
+            <rect x="10" y="120.277" width="15" height="10" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,180,0)">
+            <rect x="10" y="120.277" width="15" height="10" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,210,0)">
+            <rect x="10" y="120.277" width="15" height="10" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,240,0)">
+            <rect x="10" y="120.277" width="15" height="10" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,270,0)">
+            <rect x="10" y="120.277" width="15" height="10" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,300,0)">
+            <rect x="10" y="120.277" width="15" height="10" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,330,0)">
+            <rect x="10" y="120.277" width="15" height="10" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,360,0)">
+            <rect x="10" y="120.277" width="15" height="10" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <rect x="25" y="120.277" width="15" height="10" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+        <g transform="matrix(1,0,0,1,30,0)">
+            <rect x="25" y="120.277" width="15" height="10" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,60,0)">
+            <rect x="25" y="120.277" width="15" height="10" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,90,0)">
+            <rect x="25" y="120.277" width="15" height="10" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,120,0)">
+            <rect x="25" y="120.277" width="15" height="10" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,150,0)">
+            <rect x="25" y="120.277" width="15" height="10" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,180,0)">
+            <rect x="25" y="120.277" width="15" height="10" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,210,0)">
+            <rect x="25" y="120.277" width="15" height="10" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,240,0)">
+            <rect x="25" y="120.277" width="15" height="10" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,270,0)">
+            <rect x="25" y="120.277" width="15" height="10" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,300,0)">
+            <rect x="25" y="120.277" width="15" height="10" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,330,0)">
+            <rect x="25" y="120.277" width="15" height="10" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,360,0)">
+            <rect x="25" y="120.277" width="15" height="10" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+        </g>
+    </g>
+    <g transform="matrix(1,0,0,1,-30,-49.7228)">
+        <g transform="matrix(1,0,0,1,28.0614,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">y</text>
+        </g>
+        <g transform="matrix(1,0,0,1,58.0614,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">y</text>
+        </g>
+        <g transform="matrix(1,0,0,1,88.0614,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">y</text>
+        </g>
+        <g transform="matrix(1,0,0,1,118.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">y</text>
+        </g>
+        <g transform="matrix(1,0,0,1,148.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">y</text>
+        </g>
+        <g transform="matrix(1,0,0,1,178.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">y</text>
+        </g>
+        <g transform="matrix(1,0,0,1,208.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">y</text>
+        </g>
+        <g transform="matrix(1,0,0,1,238.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">y</text>
+        </g>
+        <g transform="matrix(1,0,0,1,268.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">y</text>
+        </g>
+        <g transform="matrix(1,0,0,1,298.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">y</text>
+        </g>
+        <g transform="matrix(1,0,0,1,328.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">y</text>
+        </g>
+        <g transform="matrix(1,0,0,1,358.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">y</text>
+        </g>
+        <g transform="matrix(1,0,0,1,388.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">y</text>
+        </g>
+        <g transform="matrix(1,0,0,1,38.0614,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">z</text>
+        </g>
+        <g transform="matrix(1,0,0,1,68.0614,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">z</text>
+        </g>
+        <g transform="matrix(1,0,0,1,98.0614,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">z</text>
+        </g>
+        <g transform="matrix(1,0,0,1,128.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">z</text>
+        </g>
+        <g transform="matrix(1,0,0,1,158.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">z</text>
+        </g>
+        <g transform="matrix(1,0,0,1,188.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">z</text>
+        </g>
+        <g transform="matrix(1,0,0,1,218.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">z</text>
+        </g>
+        <g transform="matrix(1,0,0,1,248.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">z</text>
+        </g>
+        <g transform="matrix(1,0,0,1,278.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">z</text>
+        </g>
+        <g transform="matrix(1,0,0,1,308.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">z</text>
+        </g>
+        <g transform="matrix(1,0,0,1,338.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">z</text>
+        </g>
+        <g transform="matrix(1,0,0,1,368.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">z</text>
+        </g>
+        <g transform="matrix(1,0,0,1,398.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">z</text>
+        </g>
+        <g transform="matrix(1,0,0,1,18.0614,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">x</text>
+        </g>
+        <g transform="matrix(1,0,0,1,48.0614,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">x</text>
+        </g>
+        <g transform="matrix(1,0,0,1,78.0614,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">x</text>
+        </g>
+        <g transform="matrix(1,0,0,1,108.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">x</text>
+        </g>
+        <g transform="matrix(1,0,0,1,138.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">x</text>
+        </g>
+        <g transform="matrix(1,0,0,1,168.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">x</text>
+        </g>
+        <g transform="matrix(1,0,0,1,198.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">x</text>
+        </g>
+        <g transform="matrix(1,0,0,1,228.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">x</text>
+        </g>
+        <g transform="matrix(1,0,0,1,258.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">x</text>
+        </g>
+        <g transform="matrix(1,0,0,1,288.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">x</text>
+        </g>
+        <g transform="matrix(1,0,0,1,318.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">x</text>
+        </g>
+        <g transform="matrix(1,0,0,1,348.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">x</text>
+        </g>
+        <g transform="matrix(1,0,0,1,378.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">x</text>
+        </g>
+    </g>
+    <g transform="matrix(1,0,0,1,-30,-19.7228)">
+        <g transform="matrix(1,0,0,1,28.0614,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">y</text>
+        </g>
+        <g transform="matrix(1,0,0,1,58.0614,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">y</text>
+        </g>
+        <g transform="matrix(1,0,0,1,88.0614,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">y</text>
+        </g>
+        <g transform="matrix(1,0,0,1,118.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">y</text>
+        </g>
+        <g transform="matrix(1,0,0,1,148.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">y</text>
+        </g>
+        <g transform="matrix(1,0,0,1,178.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">y</text>
+        </g>
+        <g transform="matrix(1,0,0,1,208.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">y</text>
+        </g>
+        <g transform="matrix(1,0,0,1,238.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">y</text>
+        </g>
+        <g transform="matrix(1,0,0,1,268.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">y</text>
+        </g>
+        <g transform="matrix(1,0,0,1,298.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">y</text>
+        </g>
+        <g transform="matrix(1,0,0,1,328.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">y</text>
+        </g>
+        <g transform="matrix(1,0,0,1,358.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">y</text>
+        </g>
+        <g transform="matrix(1,0,0,1,388.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">y</text>
+        </g>
+        <g transform="matrix(1,0,0,1,38.0614,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">z</text>
+        </g>
+        <g transform="matrix(1,0,0,1,68.0614,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">z</text>
+        </g>
+        <g transform="matrix(1,0,0,1,98.0614,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">z</text>
+        </g>
+        <g transform="matrix(1,0,0,1,128.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">z</text>
+        </g>
+        <g transform="matrix(1,0,0,1,158.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">z</text>
+        </g>
+        <g transform="matrix(1,0,0,1,188.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">z</text>
+        </g>
+        <g transform="matrix(1,0,0,1,218.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">z</text>
+        </g>
+        <g transform="matrix(1,0,0,1,248.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">z</text>
+        </g>
+        <g transform="matrix(1,0,0,1,278.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">z</text>
+        </g>
+        <g transform="matrix(1,0,0,1,308.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">z</text>
+        </g>
+        <g transform="matrix(1,0,0,1,338.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">z</text>
+        </g>
+        <g transform="matrix(1,0,0,1,368.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">z</text>
+        </g>
+        <g transform="matrix(1,0,0,1,398.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">z</text>
+        </g>
+        <g transform="matrix(1,0,0,1,18.0614,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">x</text>
+        </g>
+        <g transform="matrix(1,0,0,1,48.0614,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">x</text>
+        </g>
+        <g transform="matrix(1,0,0,1,78.0614,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">x</text>
+        </g>
+        <g transform="matrix(1,0,0,1,108.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">x</text>
+        </g>
+        <g transform="matrix(1,0,0,1,138.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">x</text>
+        </g>
+        <g transform="matrix(1,0,0,1,168.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">x</text>
+        </g>
+        <g transform="matrix(1,0,0,1,198.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">x</text>
+        </g>
+        <g transform="matrix(1,0,0,1,228.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">x</text>
+        </g>
+        <g transform="matrix(1,0,0,1,258.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">x</text>
+        </g>
+        <g transform="matrix(1,0,0,1,288.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">x</text>
+        </g>
+        <g transform="matrix(1,0,0,1,318.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">x</text>
+        </g>
+        <g transform="matrix(1,0,0,1,348.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">x</text>
+        </g>
+        <g transform="matrix(1,0,0,1,378.061,42.3808)">
+            <text x="24.509px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">x</text>
+        </g>
+    </g>
+    <g transform="matrix(1,0,0,1,-30,10.804)">
+        <g transform="matrix(1,0,0,1,28.0614,42.3808)">
+            <text x="24.229px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">g</text>
+        </g>
+        <g transform="matrix(1,0,0,1,58.0614,42.3808)">
+            <text x="24.229px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">g</text>
+        </g>
+        <g transform="matrix(1,0,0,1,88.0614,42.3808)">
+            <text x="24.229px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">g</text>
+        </g>
+        <g transform="matrix(1,0,0,1,118.061,42.3808)">
+            <text x="24.229px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">g</text>
+        </g>
+        <g transform="matrix(1,0,0,1,148.061,42.3808)">
+            <text x="24.229px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">g</text>
+        </g>
+        <g transform="matrix(1,0,0,1,178.061,42.3808)">
+            <text x="24.229px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">g</text>
+        </g>
+        <g transform="matrix(1,0,0,1,208.061,42.3808)">
+            <text x="24.229px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">g</text>
+        </g>
+        <g transform="matrix(1,0,0,1,238.061,42.3808)">
+            <text x="24.229px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">g</text>
+        </g>
+        <g transform="matrix(1,0,0,1,268.061,42.3808)">
+            <text x="24.229px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">g</text>
+        </g>
+        <g transform="matrix(1,0,0,1,298.061,42.3808)">
+            <text x="24.229px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">g</text>
+        </g>
+        <g transform="matrix(1,0,0,1,328.061,42.3808)">
+            <text x="24.229px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">g</text>
+        </g>
+        <g transform="matrix(1,0,0,1,358.061,42.3808)">
+            <text x="24.229px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">g</text>
+        </g>
+        <g transform="matrix(1,0,0,1,388.061,42.3808)">
+            <text x="24.229px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">g</text>
+        </g>
+        <g transform="matrix(1,0,0,1,38.0614,42.3808)">
+            <text x="24.229px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">b</text>
+        </g>
+        <g transform="matrix(1,0,0,1,68.0614,42.3808)">
+            <text x="24.229px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">b</text>
+        </g>
+        <g transform="matrix(1,0,0,1,98.0614,42.3808)">
+            <text x="24.229px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">b</text>
+        </g>
+        <g transform="matrix(1,0,0,1,128.061,42.3808)">
+            <text x="24.229px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">b</text>
+        </g>
+        <g transform="matrix(1,0,0,1,158.061,42.3808)">
+            <text x="24.229px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">b</text>
+        </g>
+        <g transform="matrix(1,0,0,1,188.061,42.3808)">
+            <text x="24.229px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">b</text>
+        </g>
+        <g transform="matrix(1,0,0,1,218.061,42.3808)">
+            <text x="24.229px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">b</text>
+        </g>
+        <g transform="matrix(1,0,0,1,248.061,42.3808)">
+            <text x="24.229px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">b</text>
+        </g>
+        <g transform="matrix(1,0,0,1,278.061,42.3808)">
+            <text x="24.229px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">b</text>
+        </g>
+        <g transform="matrix(1,0,0,1,308.061,42.3808)">
+            <text x="24.229px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">b</text>
+        </g>
+        <g transform="matrix(1,0,0,1,338.061,42.3808)">
+            <text x="24.229px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">b</text>
+        </g>
+        <g transform="matrix(1,0,0,1,368.061,42.3808)">
+            <text x="24.229px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">b</text>
+        </g>
+        <g transform="matrix(1,0,0,1,398.061,42.3808)">
+            <text x="24.229px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">b</text>
+        </g>
+        <g transform="matrix(1,0,0,1,18.0614,42.3808)">
+            <text x="25.344px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">r</text>
+        </g>
+        <g transform="matrix(1,0,0,1,48.0614,42.3808)">
+            <text x="25.344px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">r</text>
+        </g>
+        <g transform="matrix(1,0,0,1,78.0614,42.3808)">
+            <text x="25.344px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">r</text>
+        </g>
+        <g transform="matrix(1,0,0,1,108.061,42.3808)">
+            <text x="25.344px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">r</text>
+        </g>
+        <g transform="matrix(1,0,0,1,138.061,42.3808)">
+            <text x="25.344px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">r</text>
+        </g>
+        <g transform="matrix(1,0,0,1,168.061,42.3808)">
+            <text x="25.344px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">r</text>
+        </g>
+        <g transform="matrix(1,0,0,1,198.061,42.3808)">
+            <text x="25.344px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">r</text>
+        </g>
+        <g transform="matrix(1,0,0,1,228.061,42.3808)">
+            <text x="25.344px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">r</text>
+        </g>
+        <g transform="matrix(1,0,0,1,258.061,42.3808)">
+            <text x="25.344px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">r</text>
+        </g>
+        <g transform="matrix(1,0,0,1,288.061,42.3808)">
+            <text x="25.344px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">r</text>
+        </g>
+        <g transform="matrix(1,0,0,1,318.061,42.3808)">
+            <text x="25.344px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">r</text>
+        </g>
+        <g transform="matrix(1,0,0,1,348.061,42.3808)">
+            <text x="25.344px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">r</text>
+        </g>
+        <g transform="matrix(1,0,0,1,378.061,42.3808)">
+            <text x="25.344px" y="44.619px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">r</text>
+        </g>
+    </g>
+    <g>
+        <g transform="matrix(1,0,0,1,-0.234618,0.93567)">
+            <text x="14.993px" y="127.064px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">u</text>
+        </g>
+        <g transform="matrix(1,0,0,1,29.7654,0.93567)">
+            <text x="14.993px" y="127.064px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">u</text>
+        </g>
+        <g transform="matrix(1,0,0,1,59.7654,0.93567)">
+            <text x="14.993px" y="127.064px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">u</text>
+        </g>
+        <g transform="matrix(1,0,0,1,89.7654,0.93567)">
+            <text x="14.993px" y="127.064px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">u</text>
+        </g>
+        <g transform="matrix(1,0,0,1,119.765,0.93567)">
+            <text x="14.993px" y="127.064px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">u</text>
+        </g>
+        <g transform="matrix(1,0,0,1,149.765,0.93567)">
+            <text x="14.993px" y="127.064px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">u</text>
+        </g>
+        <g transform="matrix(1,0,0,1,179.765,0.93567)">
+            <text x="14.993px" y="127.064px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">u</text>
+        </g>
+        <g transform="matrix(1,0,0,1,209.765,0.93567)">
+            <text x="14.993px" y="127.064px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">u</text>
+        </g>
+        <g transform="matrix(1,0,0,1,239.765,0.93567)">
+            <text x="14.993px" y="127.064px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">u</text>
+        </g>
+        <g transform="matrix(1,0,0,1,269.765,0.93567)">
+            <text x="14.993px" y="127.064px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">u</text>
+        </g>
+        <g transform="matrix(1,0,0,1,299.765,0.93567)">
+            <text x="14.993px" y="127.064px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">u</text>
+        </g>
+        <g transform="matrix(1,0,0,1,329.765,0.93567)">
+            <text x="14.993px" y="127.064px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">u</text>
+        </g>
+        <g transform="matrix(1,0,0,1,359.765,0.93567)">
+            <text x="14.993px" y="127.064px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">u</text>
+        </g>
+        <g transform="matrix(1,0,0,1,14.7654,1.12122)">
+            <text x="15.274px" y="127.064px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">v</text>
+        </g>
+        <g transform="matrix(1,0,0,1,44.7654,1.12122)">
+            <text x="15.274px" y="127.064px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">v</text>
+        </g>
+        <g transform="matrix(1,0,0,1,74.7654,1.12122)">
+            <text x="15.274px" y="127.064px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">v</text>
+        </g>
+        <g transform="matrix(1,0,0,1,104.765,1.12122)">
+            <text x="15.274px" y="127.064px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">v</text>
+        </g>
+        <g transform="matrix(1,0,0,1,134.765,1.12122)">
+            <text x="15.274px" y="127.064px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">v</text>
+        </g>
+        <g transform="matrix(1,0,0,1,164.765,1.12122)">
+            <text x="15.274px" y="127.064px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">v</text>
+        </g>
+        <g transform="matrix(1,0,0,1,194.765,1.12122)">
+            <text x="15.274px" y="127.064px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">v</text>
+        </g>
+        <g transform="matrix(1,0,0,1,224.765,1.12122)">
+            <text x="15.274px" y="127.064px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">v</text>
+        </g>
+        <g transform="matrix(1,0,0,1,254.765,1.12122)">
+            <text x="15.274px" y="127.064px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">v</text>
+        </g>
+        <g transform="matrix(1,0,0,1,284.765,1.12122)">
+            <text x="15.274px" y="127.064px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">v</text>
+        </g>
+        <g transform="matrix(1,0,0,1,314.765,1.12122)">
+            <text x="15.274px" y="127.064px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">v</text>
+        </g>
+        <g transform="matrix(1,0,0,1,344.765,1.12122)">
+            <text x="15.274px" y="127.064px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">v</text>
+        </g>
+        <g transform="matrix(1,0,0,1,374.765,1.12122)">
+            <text x="15.274px" y="127.064px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:10px;">v</text>
+        </g>
+    </g>
+    <g transform="matrix(0.971429,0,0,1,4.62857,-9.72281)">
+        <rect x="127" y="26" width="35" height="117" style="fill:rgb(228,0,168);fill-opacity:0.196078;stroke:black;stroke-width:0.2px;"/>
+    </g>
+    <g transform="matrix(1,0,0,1,-5.68434e-14,-9.72281)">
+        <text x="128.83px" y="22.888px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:9px;">vertex 4</text>
+    </g>
+</svg>

+ 101 - 0
threejs/lessons/resources/threejs-custom-buffergeometry.js

@@ -0,0 +1,101 @@
+'use strict';
+
+/* global THREE, threejsLessonUtils */
+
+{
+  const loader = new THREE.TextureLoader();
+  const texture = loader.load('../resources/images/star-light.png');
+  texture.wrapS = THREE.RepeatWrapping;
+  texture.wrapT = THREE.RepeatWrapping;
+  texture.repeat.set(3, 1);
+
+  function makeMesh(geometry) {
+    const material = new THREE.MeshPhongMaterial({
+      color: 'hsl(300,50%,50%)',
+      side: THREE.DoubleSide,
+      map: texture,
+    });
+    return new THREE.Mesh(geometry, material);
+  }
+
+  threejsLessonUtils.addDiagrams({
+    geometryCylinder: {
+      create() {
+        const numSegments = 24;
+        const geometry = new THREE.Geometry();
+        const wrap = ndx => ndx % (numSegments * 2);
+        for (let s = 0; s < numSegments; ++s) {
+          const u = s / numSegments;
+          const a = u * Math.PI * 2;
+          const x = Math.sin(a);
+          const z = Math.cos(a);
+          geometry.vertices.push(new THREE.Vector3(x, -1, z));
+          geometry.vertices.push(new THREE.Vector3(x,  1, z));
+
+          // share the start and end positions
+          const ndx = s * 2;
+          geometry.faces.push(new THREE.Face3(ndx, wrap(ndx + 2), ndx + 1));
+          geometry.faces.push(new THREE.Face3(ndx + 1, wrap(ndx + 2), wrap(ndx + 3)));
+
+          const u2 = (s + 1) / numSegments;
+          geometry.faceVertexUvs[0].push([
+            new THREE.Vector2(u, 0),
+            new THREE.Vector2(u2, 0),
+            new THREE.Vector2(u, 1),
+          ]);
+          geometry.faceVertexUvs[0].push([
+            new THREE.Vector2(u, 1),
+            new THREE.Vector2(u2, 0),
+            new THREE.Vector2(u2, 1),
+          ]);
+        }
+
+        geometry.computeVertexNormals();
+        geometry.scale(5, 5, 5);
+        return makeMesh(geometry);
+      },
+    },
+    bufferGeometryCylinder: {
+      create() {
+        const numSegments = 24;
+        const positions = [];
+        const uvs = [];
+        for (let s = 0; s <= numSegments; ++s) {
+          const u = s / numSegments;
+          const a = u * Math.PI * 2;
+          const x = Math.sin(a);
+          const z = Math.cos(a);
+          positions.push(x, -1, z);
+          positions.push(x,  1, z);
+          uvs.push(u, 0);
+          uvs.push(u, 1);
+        }
+
+        const indices = [];
+        for (let s = 0; s < numSegments; ++s) {
+          const ndx = s * 2;
+          indices.push(
+            ndx, ndx + 2, ndx + 1,
+            ndx + 1, ndx + 2, ndx + 3,
+          );
+        }
+
+        const positionNumComponents = 3;
+        const uvNumComponents = 2;
+        const geometry = new THREE.BufferGeometry();
+        geometry.addAttribute(
+            'position',
+            new THREE.BufferAttribute(new Float32Array(positions), positionNumComponents));
+        geometry.addAttribute(
+            'uv',
+            new THREE.BufferAttribute(new Float32Array(uvs), uvNumComponents));
+
+        geometry.setIndex(indices);
+        geometry.computeVertexNormals();
+        geometry.scale(5, 5, 5);
+        return makeMesh(geometry);
+      },
+    },
+  });
+
+}

+ 263 - 0
threejs/lessons/resources/threejs-geometry.svg

@@ -0,0 +1,263 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg width="100%" height="100%" viewBox="0 0 460 380" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
+    <g transform="matrix(1,0,0,1,-20,0)">
+        <g transform="matrix(1,0,0,1,-20,-20)">
+            <rect x="60" y="60" width="40" height="40" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,-0.853483,1.27345)">
+            <text x="49.05px" y="60.575px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">x,y<tspan x="63.493px 66.827px " y="60.575px 60.575px ">,z</tspan></text>
+        </g>
+    </g>
+    <g transform="matrix(1,0,0,1,20,0)">
+        <g transform="matrix(1,0,0,1,-20,-20)">
+            <rect x="60" y="60" width="40" height="40" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,-0.853483,1.27345)">
+            <text x="49.05px" y="60.575px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">x,y<tspan x="63.493px 66.827px " y="60.575px 60.575px ">,z</tspan></text>
+        </g>
+    </g>
+    <g transform="matrix(1,0,0,1,60,0)">
+        <g transform="matrix(1,0,0,1,-20,-20)">
+            <rect x="60" y="60" width="40" height="40" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,-0.853483,1.27345)">
+            <text x="49.05px" y="60.575px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">x,y<tspan x="63.493px 66.827px " y="60.575px 60.575px ">,z</tspan></text>
+        </g>
+    </g>
+    <g transform="matrix(1,0,0,1,100,0)">
+        <g transform="matrix(1,0,0,1,-20,-20)">
+            <rect x="60" y="60" width="40" height="40" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,-0.853483,1.27345)">
+            <text x="49.05px" y="60.575px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">x,y<tspan x="63.493px 66.827px " y="60.575px 60.575px ">,z</tspan></text>
+        </g>
+    </g>
+    <g transform="matrix(1,0,0,1,140,0)">
+        <g transform="matrix(1,0,0,1,-20,-20)">
+            <rect x="60" y="60" width="40" height="40" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,-0.853483,1.27345)">
+            <text x="49.05px" y="60.575px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">x,y<tspan x="63.493px 66.827px " y="60.575px 60.575px ">,z</tspan></text>
+        </g>
+    </g>
+    <g transform="matrix(1,0,0,1,180,0)">
+        <g transform="matrix(1,0,0,1,-20,-20)">
+            <rect x="60" y="60" width="40" height="40" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,-0.853483,1.27345)">
+            <text x="49.05px" y="60.575px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">x,y<tspan x="63.493px 66.827px " y="60.575px 60.575px ">,z</tspan></text>
+        </g>
+    </g>
+    <g transform="matrix(1,0,0,1,220,0)">
+        <g transform="matrix(1,0,0,1,-20,-20)">
+            <rect x="60" y="60" width="40" height="40" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,-0.853483,1.27345)">
+            <text x="49.05px" y="60.575px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">x,y<tspan x="63.493px 66.827px " y="60.575px 60.575px ">,z</tspan></text>
+        </g>
+    </g>
+    <g transform="matrix(1,0,0,1,260,0)">
+        <g transform="matrix(1,0,0,1,-20,-20)">
+            <rect x="60" y="60" width="40" height="40" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,-0.853483,1.27345)">
+            <text x="49.05px" y="60.575px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">x,y<tspan x="63.493px 66.827px " y="60.575px 60.575px ">,z</tspan></text>
+        </g>
+    </g>
+    <g transform="matrix(1,0,0,1,300,0)">
+        <g transform="matrix(1,0,0,1,-20,-20)">
+            <rect x="60" y="60" width="40" height="40" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,-0.853483,1.27345)">
+            <text x="49.05px" y="60.575px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">x,y<tspan x="63.493px 66.827px " y="60.575px 60.575px ">,z</tspan></text>
+        </g>
+    </g>
+    <g transform="matrix(1,0,0,1,340,0)">
+        <g transform="matrix(1,0,0,1,-20,-20)">
+            <rect x="60" y="60" width="40" height="40" style="fill:rgb(0,212,255);stroke:black;stroke-width:0.2px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,-0.853483,1.27345)">
+            <text x="49.05px" y="60.575px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">x,y<tspan x="63.493px 66.827px " y="60.575px 60.575px ">,z</tspan></text>
+        </g>
+    </g>
+    <g>
+        <g transform="matrix(1.28571,0,0,1,-11.4286,0)">
+            <rect x="40" y="120" width="140" height="60" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.17px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,15.4797,-10.6675)">
+            <text x="33.425px" y="151.325px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">indices: 0, 1, 2</text>
+            <text x="33.425px" y="163.717px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">vertexNormals: xyz, xyz, xyz</text>
+            <text x="33.425px" y="176.11px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">vertexColors: rgb, rgb, rgb</text>
+        </g>
+    </g>
+    <g transform="matrix(1,0,0,1,-5.68434e-14,60)">
+        <g transform="matrix(1.28571,0,0,1,-11.4286,0)">
+            <rect x="40" y="120" width="140" height="60" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.17px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,15.4797,-10.6675)">
+            <text x="33.425px" y="151.325px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">indices: 0, 3, 1</text>
+            <text x="33.425px" y="163.717px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">vertexNormals: xyz, xyz, xyz</text>
+            <text x="33.425px" y="176.11px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">vertexColors: rgb, rgb, rgb</text>
+        </g>
+    </g>
+    <g transform="matrix(1,0,0,1,-5.68434e-14,120)">
+        <g transform="matrix(1.28571,0,0,1,-11.4286,0)">
+            <rect x="40" y="120" width="140" height="60" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.17px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,15.4797,-10.6675)">
+            <text x="33.425px" y="151.325px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">indices: 4, 5, 6</text>
+            <text x="33.425px" y="163.717px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">vertexNormals: xyz, xyz, xyz</text>
+            <text x="33.425px" y="176.11px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">vertexColors: rgb, rgb, rgb</text>
+        </g>
+    </g>
+    <g transform="matrix(1,0,0,1,-5.68434e-14,180)">
+        <g transform="matrix(1.28571,0,0,1,-11.4286,0)">
+            <rect x="40" y="120" width="140" height="60" style="fill:rgb(0,162,255);stroke:black;stroke-width:0.17px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,15.4797,-10.6675)">
+            <text x="33.425px" y="151.325px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">indices: 4, 7, 5</text>
+            <text x="33.425px" y="163.717px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">vertexNormals: xyz, xyz, xyz</text>
+            <text x="33.425px" y="176.11px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">vertexColors: rgb, rgb, rgb</text>
+        </g>
+    </g>
+    <rect x="40" y="100" width="180" height="20" style="fill:rgb(0,145,228);stroke:black;stroke-width:0.2px;"/>
+    <g transform="matrix(1,0,0,1,-16.5283,0.403108)">
+        <text x="130.963px" y="113.833px" style="font-family:'Arial-BoldMT', 'Arial', sans-serif;font-weight:700;font-size:12px;">faces</text>
+    </g>
+    <g transform="matrix(1,0,0,1.53846,-20,-21.5385)">
+        <rect x="40" y="27" width="400" height="13" style="fill:rgb(0,180,216);stroke:black;stroke-width:0.15px;"/>
+    </g>
+    <g transform="matrix(1,0,0,1,-19.6069,-0.342976)">
+        <text x="186.164px" y="33.448px" style="font-family:'Arial-BoldMT', 'Arial', sans-serif;font-weight:700;font-size:12px;">vertices(positions)</text>
+    </g>
+    <g transform="matrix(1,0,0,1,-20,0)">
+        <rect x="280" y="120" width="140" height="240" style="fill:rgb(3,146,106);stroke:black;stroke-width:0.2px;"/>
+    </g>
+    <g transform="matrix(1,0,0,1,10,0)">
+        <rect x="250" y="120" width="140" height="60" style="fill:none;"/>
+        <g transform="matrix(1,0,0,1.1,-30,-12.5)">
+            <rect x="285" y="125" width="130" height="50" style="fill:rgb(10,197,145);stroke:black;stroke-width:0.19px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,0,10)">
+            <g transform="matrix(1,0,0,1,20,0)">
+                <rect x="240" y="120" width="40" height="40" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+            </g>
+            <g transform="matrix(1,0,0,1,21.749,3.84696)">
+                <text x="249.934px" y="138.415px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">u,v</text>
+            </g>
+        </g>
+        <g transform="matrix(1,0,0,1,40,10)">
+            <g transform="matrix(1,0,0,1,20,0)">
+                <rect x="240" y="120" width="40" height="40" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+            </g>
+            <g transform="matrix(1,0,0,1,21.749,3.84696)">
+                <text x="249.934px" y="138.415px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">u,v</text>
+            </g>
+        </g>
+        <g transform="matrix(1,0,0,1,80,10)">
+            <g transform="matrix(1,0,0,1,20,0)">
+                <rect x="240" y="120" width="40" height="40" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+            </g>
+            <g transform="matrix(1,0,0,1,21.749,3.84696)">
+                <text x="249.934px" y="138.415px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">u,v</text>
+            </g>
+        </g>
+    </g>
+    <g transform="matrix(1,0,0,1,10,60)">
+        <rect x="250" y="120" width="140" height="60" style="fill:none;"/>
+        <g transform="matrix(1,0,0,1.2,-30,-30)">
+            <rect x="285" y="125" width="130" height="50" style="fill:rgb(10,197,145);stroke:black;stroke-width:0.18px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,0,10)">
+            <g transform="matrix(1,0,0,1,20,0)">
+                <rect x="240" y="120" width="40" height="40" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+            </g>
+            <g transform="matrix(1,0,0,1,21.749,3.84696)">
+                <text x="249.934px" y="138.415px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">u,v</text>
+            </g>
+        </g>
+        <g transform="matrix(1,0,0,1,40,10)">
+            <g transform="matrix(1,0,0,1,20,0)">
+                <rect x="240" y="120" width="40" height="40" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+            </g>
+            <g transform="matrix(1,0,0,1,21.749,3.84696)">
+                <text x="249.934px" y="138.415px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">u,v</text>
+            </g>
+        </g>
+        <g transform="matrix(1,0,0,1,80,10)">
+            <g transform="matrix(1,0,0,1,20,0)">
+                <rect x="240" y="120" width="40" height="40" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+            </g>
+            <g transform="matrix(1,0,0,1,21.749,3.84696)">
+                <text x="249.934px" y="138.415px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">u,v</text>
+            </g>
+        </g>
+    </g>
+    <g transform="matrix(1,0,0,1,10,120)">
+        <rect x="250" y="120" width="140" height="60" style="fill:none;"/>
+        <g transform="matrix(1,0,0,1.2,-30,-30)">
+            <rect x="285" y="125" width="130" height="50" style="fill:rgb(10,197,145);stroke:black;stroke-width:0.18px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,0,10)">
+            <g transform="matrix(1,0,0,1,20,0)">
+                <rect x="240" y="120" width="40" height="40" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+            </g>
+            <g transform="matrix(1,0,0,1,21.749,3.84696)">
+                <text x="249.934px" y="138.415px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">u,v</text>
+            </g>
+        </g>
+        <g transform="matrix(1,0,0,1,40,10)">
+            <g transform="matrix(1,0,0,1,20,0)">
+                <rect x="240" y="120" width="40" height="40" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+            </g>
+            <g transform="matrix(1,0,0,1,21.749,3.84696)">
+                <text x="249.934px" y="138.415px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">u,v</text>
+            </g>
+        </g>
+        <g transform="matrix(1,0,0,1,80,10)">
+            <g transform="matrix(1,0,0,1,20,0)">
+                <rect x="240" y="120" width="40" height="40" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+            </g>
+            <g transform="matrix(1,0,0,1,21.749,3.84696)">
+                <text x="249.934px" y="138.415px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">u,v</text>
+            </g>
+        </g>
+    </g>
+    <g transform="matrix(1,0,0,1,10,180)">
+        <rect x="250" y="120" width="140" height="60" style="fill:none;"/>
+        <g transform="matrix(1,0,0,1.1,-30,-17.5)">
+            <rect x="285" y="125" width="130" height="50" style="fill:rgb(10,197,145);stroke:black;stroke-width:0.19px;"/>
+        </g>
+        <g transform="matrix(1,0,0,1,0,10)">
+            <g transform="matrix(1,0,0,1,20,0)">
+                <rect x="240" y="120" width="40" height="40" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+            </g>
+            <g transform="matrix(1,0,0,1,21.749,3.84696)">
+                <text x="249.934px" y="138.415px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">u,v</text>
+            </g>
+        </g>
+        <g transform="matrix(1,0,0,1,40,10)">
+            <g transform="matrix(1,0,0,1,20,0)">
+                <rect x="240" y="120" width="40" height="40" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+            </g>
+            <g transform="matrix(1,0,0,1,21.749,3.84696)">
+                <text x="249.934px" y="138.415px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">u,v</text>
+            </g>
+        </g>
+        <g transform="matrix(1,0,0,1,80,10)">
+            <g transform="matrix(1,0,0,1,20,0)">
+                <rect x="240" y="120" width="40" height="40" style="fill:rgb(0,216,156);stroke:black;stroke-width:0.2px;"/>
+            </g>
+            <g transform="matrix(1,0,0,1,21.749,3.84696)">
+                <text x="249.934px" y="138.415px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">u,v</text>
+            </g>
+        </g>
+    </g>
+    <g transform="matrix(1,0,0,1,10,0)">
+        <rect x="250" y="100" width="140" height="20" style="fill:rgb(8,166,122);stroke:black;stroke-width:0.2px;"/>
+    </g>
+    <g transform="matrix(1,0,0,1,11.2642,3.15754)">
+        <text x="277.216px" y="111.137px" style="font-family:'Arial-BoldMT', 'Arial', sans-serif;font-weight:700;font-size:12px;">faceV<tspan x="308.576px 315.249px " y="111.137px 111.137px ">er</tspan>texUVs</text>
+    </g>
+</svg>

+ 462 - 0
threejs/lessons/threejs-custom-buffergeometry.md

@@ -0,0 +1,462 @@
+Title: Three.js Custom BufferGeometry
+Description: How to make your own BufferGeometry.
+
+A [previous article](threejs-custom-geometry.html) covered
+how to use `Geometry`. This article is about `BufferGeometry`.
+`BufferGeometry` is *generally* faster to start and uses
+less memory but can be harder to setup.
+
+In [the article on Geometry] we went over that to use a `Geometry` you supply an
+array of `Vector3` vertices (postions). You then make `Face3` objects specifying
+by index the 3 vertices that make each triangle of the shape you're making. To
+each `Face3` you can specify either a face normal or normals for each individual
+vertex of the face. You can also specify a face color or individual vertex
+colors. Finally you can make a parallel array of arrays of texture coordinates
+(UVs), one array for each face containing an array of UVs, one for each vertex
+of the face.
+
+<div class="threejs_center"><img src="resources/threejs-geometry.svg" style="width: 700px"></div>
+
+`BufferGeometry` on the other hand uses *named* `BufferAttribute`s
+Each `BufferAttribute` represents an array of one type of data, positions,
+normals, colors, uv, and togther the all the added `BufferAttribute`s represent
+*parallel arrays*  of all the data for each vertex.
+
+<div class="threejs_center"><img src="resources/threejs-attributes.svg" style="width: 700px"></div>
+
+Above you can see we have 4 attributes, `position`, `normal`, `color`, `uv`.
+They represent *parallel arrays* which means that the Nth set of data in each
+attribute belongs to the same vertex. Above the vertex at index = 4 is highlighted
+to show that the parallel data across all attributes defines one vertex.
+
+This brings up a point, here's a diagram of a cube with one corner highlighted.
+
+<div class="threejs_center"><img src="resources/cube-faces-vertex.svg" style="width: 500px"></div>
+
+Thinking about it that single corner needs a different normal for each face of the
+cube. It needs different UVs for each face as well. This points out the biggest difference
+between `Geometry` and `BufferGeometry`. Nothing is shared with `BufferGeomtry`.
+A single *vertex* is the combination of all of its parts. If a vertex needs any
+part to be different then it must be a different vertex.
+
+The truth is when you use `Geometry` three.js transforms it into this format.
+That is where the extra memory and time comes from when using `Geometry`. Extra
+memory for all the `Vector3`s, `Vector2`s, `Face3`s and array objects and then
+extra time to translate all of that data into parallel arrays in the form of
+`BufferAtrribute`s like above. Somtimes that makes using `Geometry` easier.
+With `BufferGeometry` is up to us to supply the data already turned into this format.
+
+As a simple example let's make a cube using `BufferGeometry`. A cube is interesting
+because it appears to share vertices at the corners but really
+does not. For our example we'll list out all the vertices with all their data
+and then convert that data into parallel arrays and finally use those to make
+`BufferAttribute`s and add them to a `BufferGeometry`.
+
+Starting with the texture coordinate example from [the previous article](threejs-custom-geometry.html) we've delete all the code related to setting up
+a `Geometry`. Then we list all the data needed for the cube. Remember again
+that if a vertex has any unique parts it has to be a separate vertex. As such
+to make a cube requires 36 vertex. 2 triangles per face, 3 vertices per triangle,
+6 faces = 36 vertices
+
+```js
+const vertices = [
+  // front
+  { pos: [-1, -1,  1], norm: [ 0,  0,  1], uv: [0, 1], },
+  { pos: [ 1, -1,  1], norm: [ 0,  0,  1], uv: [1, 1], },
+  { pos: [-1,  1,  1], norm: [ 0,  0,  1], uv: [0, 0], },
+
+  { pos: [-1,  1,  1], norm: [ 0,  0,  1], uv: [0, 0], },
+  { pos: [ 1, -1,  1], norm: [ 0,  0,  1], uv: [1, 1], },
+  { pos: [ 1,  1,  1], norm: [ 0,  0,  1], uv: [1, 0], },
+  // right
+  { pos: [ 1, -1,  1], norm: [ 1,  0,  0], uv: [0, 1], },
+  { pos: [ 1, -1, -1], norm: [ 1,  0,  0], uv: [1, 1], },
+  { pos: [ 1,  1,  1], norm: [ 1,  0,  0], uv: [0, 0], },
+
+  { pos: [ 1,  1,  1], norm: [ 1,  0,  0], uv: [0, 0], },
+  { pos: [ 1, -1, -1], norm: [ 1,  0,  0], uv: [1, 1], },
+  { pos: [ 1,  1, -1], norm: [ 1,  0,  0], uv: [1, 0], },
+  // back
+  { pos: [ 1, -1, -1], norm: [ 0,  0, -1], uv: [0, 1], },
+  { pos: [-1, -1, -1], norm: [ 0,  0, -1], uv: [1, 1], },
+  { pos: [ 1,  1, -1], norm: [ 0,  0, -1], uv: [0, 0], },
+
+  { pos: [ 1,  1, -1], norm: [ 0,  0, -1], uv: [0, 0], },
+  { pos: [-1, -1, -1], norm: [ 0,  0, -1], uv: [1, 1], },
+  { pos: [-1,  1, -1], norm: [ 0,  0, -1], uv: [1, 0], },
+  // left
+  { pos: [-1, -1, -1], norm: [-1,  0,  0], uv: [0, 1], },
+  { pos: [-1, -1,  1], norm: [-1,  0,  0], uv: [1, 1], },
+  { pos: [-1,  1, -1], norm: [-1,  0,  0], uv: [0, 0], },
+
+  { pos: [-1,  1, -1], norm: [-1,  0,  0], uv: [0, 0], },
+  { pos: [-1, -1,  1], norm: [-1,  0,  0], uv: [1, 1], },
+  { pos: [-1,  1,  1], norm: [-1,  0,  0], uv: [1, 0], },
+  // top
+  { pos: [ 1,  1, -1], norm: [ 0,  1,  0], uv: [0, 1], },
+  { pos: [-1,  1, -1], norm: [ 0,  1,  0], uv: [1, 1], },
+  { pos: [ 1,  1,  1], norm: [ 0,  1,  0], uv: [0, 0], },
+
+  { pos: [ 1,  1,  1], norm: [ 0,  1,  0], uv: [0, 0], },
+  { pos: [-1,  1, -1], norm: [ 0,  1,  0], uv: [1, 1], },
+  { pos: [-1,  1,  1], norm: [ 0,  1,  0], uv: [1, 0], },
+  // bottom
+  { pos: [ 1, -1,  1], norm: [ 0, -1,  0], uv: [0, 1], },
+  { pos: [-1, -1,  1], norm: [ 0, -1,  0], uv: [1, 1], },
+  { pos: [ 1, -1, -1], norm: [ 0, -1,  0], uv: [0, 0], },
+
+  { pos: [ 1, -1, -1], norm: [ 0, -1,  0], uv: [0, 0], },
+  { pos: [-1, -1,  1], norm: [ 0, -1,  0], uv: [1, 1], },
+  { pos: [-1, -1, -1], norm: [ 0, -1,  0], uv: [1, 0], },
+];
+```
+
+We can then translate all of that into 3 parallel arrays
+
+```js
+const positions = [];
+const normals = [];
+const uvs = [];
+for (const vertex of vertices) {
+  positions.push(...vertex.pos);
+  normals.push(...vertex.norm);
+  uvs.push(...vertex.uv);
+}
+```
+
+Finally we can create a `BufferGeometry` and then a `BufferAttribute` for each array 
+and add it to the `BufferGeometry`.
+
+```js
+  const geometry = new THREE.BufferGeometry();
+  const positionNumComponents = 3;
+  const normalNumComponents = 3;
+  const uvNumComponents = 2;
+  geometry.addAttribute(
+      'position',
+      new THREE.BufferAttribute(new Float32Array(positions), positionNumComponents));
+  geometry.addAttribute(
+      'normal',
+      new THREE.BufferAttribute(new Float32Array(normals), normalNumComponents));
+  geometry.addAttribute(
+      'uv',
+      new THREE.BufferAttribute(new Float32Array(uvs), uvNumComponents));
+```
+
+Note that the names are sigificant. You must name your attributes the names
+that match what three.js expects (unless you are creating a custom shader).
+In this case `position`, `normal`, and `uv`. If you want vertex colors then
+name your attribtue `color`.
+
+Above we created 3 JavaScript native arrays, `positions`, `normals` and `uvs`.
+We then convert those into
+[TypedArrays](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray)
+of type `Float32Array`. A `BufferAttribute` requires a typedarray not a native
+array. A `BufferAttribute` also requres you tell it how many components there
+are per vertex. For the positions and normals we have 3 components per vertex,
+x, y, and z. For the UVs we have 2, u and v.
+
+{{{example url="../threejs-custom-buffergeometry-cube.html"}}}
+
+That's a lot of data. A small thing we can do is use indices to reference
+the vertices. Looking back at our cube data, each face is made from 2 triangles
+with 3 vertices each, 6 vertices total, but 2 of those vertices are exactly the same; 
+The same position, the same normal, and the same uv. 
+So, we can remove the matching vertices and then
+reference them by index. First we remove the matching vertices.
+
+```js
+const vertices = [
+  // front
+  { pos: [-1, -1,  1], norm: [ 0,  0,  1], uv: [0, 1], }, // 0
+  { pos: [ 1, -1,  1], norm: [ 0,  0,  1], uv: [1, 1], }, // 1
+  { pos: [-1,  1,  1], norm: [ 0,  0,  1], uv: [0, 0], }, // 2
+-
+-  { pos: [-1,  1,  1], norm: [ 0,  0,  1], uv: [0, 0], },
+-  { pos: [ 1, -1,  1], norm: [ 0,  0,  1], uv: [1, 1], },
+  { pos: [ 1,  1,  1], norm: [ 0,  0,  1], uv: [1, 0], }, // 3
+  // right
+  { pos: [ 1, -1,  1], norm: [ 1,  0,  0], uv: [0, 1], }, // 4
+  { pos: [ 1, -1, -1], norm: [ 1,  0,  0], uv: [1, 1], }, // 5
+-
+-  { pos: [ 1,  1,  1], norm: [ 1,  0,  0], uv: [0, 0], },
+-  { pos: [ 1, -1, -1], norm: [ 1,  0,  0], uv: [1, 1], },
+  { pos: [ 1,  1,  1], norm: [ 1,  0,  0], uv: [0, 0], }, // 6
+  { pos: [ 1,  1, -1], norm: [ 1,  0,  0], uv: [1, 0], }, // 7
+  // back
+  { pos: [ 1, -1, -1], norm: [ 0,  0, -1], uv: [0, 1], }, // 8
+  { pos: [-1, -1, -1], norm: [ 0,  0, -1], uv: [1, 1], }, // 9
+-
+-  { pos: [ 1,  1, -1], norm: [ 0,  0, -1], uv: [0, 0], },
+-  { pos: [-1, -1, -1], norm: [ 0,  0, -1], uv: [1, 1], },
+  { pos: [ 1,  1, -1], norm: [ 0,  0, -1], uv: [0, 0], }, // 10
+  { pos: [-1,  1, -1], norm: [ 0,  0, -1], uv: [1, 0], }, // 11
+  // left
+  { pos: [-1, -1, -1], norm: [-1,  0,  0], uv: [0, 1], }, // 12
+  { pos: [-1, -1,  1], norm: [-1,  0,  0], uv: [1, 1], }, // 13
+-
+-  { pos: [-1,  1, -1], norm: [-1,  0,  0], uv: [0, 0], },
+-  { pos: [-1, -1,  1], norm: [-1,  0,  0], uv: [1, 1], },
+  { pos: [-1,  1, -1], norm: [-1,  0,  0], uv: [0, 0], }, // 14
+  { pos: [-1,  1,  1], norm: [-1,  0,  0], uv: [1, 0], }, // 15
+  // top
+  { pos: [ 1,  1, -1], norm: [ 0,  1,  0], uv: [0, 1], }, // 16
+  { pos: [-1,  1, -1], norm: [ 0,  1,  0], uv: [1, 1], }, // 17
+-
+-  { pos: [ 1,  1,  1], norm: [ 0,  1,  0], uv: [0, 0], },
+-  { pos: [-1,  1, -1], norm: [ 0,  1,  0], uv: [1, 1], },
+  { pos: [ 1,  1,  1], norm: [ 0,  1,  0], uv: [0, 0], }, // 18
+  { pos: [-1,  1,  1], norm: [ 0,  1,  0], uv: [1, 0], }, // 19
+  // bottom
+  { pos: [ 1, -1,  1], norm: [ 0, -1,  0], uv: [0, 1], }, // 20
+  { pos: [-1, -1,  1], norm: [ 0, -1,  0], uv: [1, 1], }, // 21
+-
+-  { pos: [ 1, -1, -1], norm: [ 0, -1,  0], uv: [0, 0], },
+-  { pos: [-1, -1,  1], norm: [ 0, -1,  0], uv: [1, 1], },
+  { pos: [ 1, -1, -1], norm: [ 0, -1,  0], uv: [0, 0], }, // 22
+  { pos: [-1, -1, -1], norm: [ 0, -1,  0], uv: [1, 0], }, // 23
+];
+```
+
+So now we have 24 unique vertices. Then we specify 36 indices
+for the 36 vertices we need drawn to make 12 triangles by calling `BufferGeometry.setIndex` with an array of indicies.
+
+```js
+geometry.addAttribute(
+    'position',
+    new THREE.BufferAttribute(positions, positionNumComponents));
+geometry.addAttribute(
+    'normal',
+    new THREE.BufferAttribute(normals, normalNumComponents));
+geometry.addAttribute(
+    'uv',
+    new THREE.BufferAttribute(uvs, uvNumComponents));
+
++geometry.setIndex([
++   0,  1,  2,   2,  1,  3,  // front
++   4,  5,  6,   6,  5,  7,  // right
++   8,  9, 10,  10,  9, 11,  // back
++  12, 13, 14,  14, 13, 15,  // left
++  16, 17, 18,  18, 17, 19,  // top
++  20, 21, 22,  22, 21, 23,  // bottom
++]);
+```
+
+{{{example url="../threejs-custom-buffergeometry-cube-indexed.html"}}}
+
+Just like `Geometry`, `BufferGeometry` has a [`computeVertexNormals`](BufferGeometry.computeVertexNormals) method for computing normals if you
+are not supplying them. Unlike the `Geometry` version of the same function,
+since positions can not be shared if any other part of a vertex is different
+the results of calling `computeVertexNormals` will be different.
+
+<div class="spread">
+  <div>
+    <div data-diagram="bufferGeometryCylinder"></div>
+    <div class="code">BufferGeometry</div>
+  </div>
+  <div>
+    <div data-diagram="geometryCylinder"></div>
+    <div class="code">Geometry</div>
+  </div>
+</div>
+
+Here are 2 cylinders where the normals were created using `computeVertexNormals`.
+If you look closely there is a seam on the left cylinder. This is because there
+is no way to share the vertices at the start and end of the cylinder since they
+require different UVs. Just a small thing to be aware of. The solution is
+to supply your own normals.
+
+We can also use [typedarrays](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray) from the start instead of native JavaScript arrays.
+The disadvantage to typedarrays is you must specify their size up front. Of
+course that's not that large of a burden but with native arrays we can just
+`push` values onto them and look at what size they end up by checking their
+`length` at the end. With typedarrays there is no push function so we need
+to do our own bookkeeping when adding values do them.
+
+In this example knowing the length up front is pretty easy since we're using
+a big block of static data to start.
+
+```js
+-const positions = [];
+-const normals = [];
+-const uvs = [];
++const numVertices = vertices.length;
++const positionNumComponents = 3;
++const normalNumComponents = 3;
++const uvNumComponents = 2;
++const positions = new Float32Array(numVertices * positionNumComponents);
++const normals = new Float32Array(numVertices * normalNumComponents);
++const uvs = new Float32Array(numVertices * uvNumComponents);
++let posNdx = 0;
++let nrmNdx = 0;
++let uvNdx = 0;
+for (const vertex of vertices) {
+-  positions.push(...vertex.pos);
+-  normals.push(...vertex.norm);
+-  uvs.push(...vertex.uv);
++  positions.set(vertex.pos, posNdx);
++  normals.set(vertex.norm, nrmNdx);
++  uvs.set(vertex.uv, uvNdx);
++  posNdx += positionNumComponents;
++  nrmNdx += normalNumComponents;
++  uvNdx += uvNumComponents;
+}
+
+geometry.addAttribute(
+    'position',
+-    new THREE.BufferAttribute(new Float32Array(positions), positionNumComponents));
++    new THREE.BufferAttribute(positions, positionNumComponents));
+geometry.addAttribute(
+    'normal',
+-    new THREE.BufferAttribute(new Float32Array(normals), normalNumComponents));
++    new THREE.BufferAttribute(normals, normalNumComponents));
+geometry.addAttribute(
+    'uv',
+-    new THREE.BufferAttribute(new Float32Array(uvs), uvNumComponents));
++    new THREE.BufferAttribute(uvs, uvNumComponents));
+
+geometry.setIndex([
+   0,  1,  2,   2,  1,  3,  // front
+   4,  5,  6,   6,  5,  7,  // right
+   8,  9, 10,  10,  9, 11,  // back
+  12, 13, 14,  14, 13, 15,  // left
+  16, 17, 18,  18, 17, 19,  // top
+  20, 21, 22,  22, 21, 23,  // bottom
+]);
+```
+
+{{{example url="../threejs-custom-buffergeometry-cube-typedarrays.html"}}}
+
+A good reason to use typedarrays is if you want to dyanmically update any
+part of the vertices. 
+
+I couldn't think of a really good example of dynamically updating the vertices
+so I decided to make a sphere and move each quad in and out from the center. Hopefully
+it's a useful example.
+
+Here's the code to generate positions and indices for a sphere. The code
+is sharing vertices within a quad but it's not sharing vertices between
+quads because we want to be able to move each quad separately.
+
+Because I'm lazy I used a small hierarchy of 3 `Object3D` objects to compute
+sphere points. How this works is explained in [the article on optimizing lots of objects](http://localhost:8080/threejs/lessons/threejs-optimize-lots-of-objects.html).
+
+```js
+function makeSpherePositions(segmentsAround, segmentsDown) {
+  const numVertices = segmentsAround * segmentsDown * 6;
+  const numComponents = 3;
+  const positions = new Float32Array(numVertices * numComponents);
+  const indices = [];
+
+  const longHelper = new THREE.Object3D();
+  const latHelper = new THREE.Object3D();
+  const pointHelper = new THREE.Object3D();
+  longHelper.add(latHelper);
+  latHelper.add(pointHelper);
+  pointHelper.position.z = 1;
+  const temp = new THREE.Vector3();
+
+  function getPoint(lat, long) {
+    latHelper.rotation.x = lat;
+    longHelper.rotation.y = long;
+    longHelper.updateMatrixWorld(true);
+    return pointHelper.getWorldPosition(temp).toArray();
+  }
+
+  let posNdx = 0;
+  let ndx = 0;
+  for (let down = 0; down < segmentsDown; ++down) {
+    const v0 = down / segmentsDown;
+    const v1 = (down + 1) / segmentsDown;
+    const lat0 = (v0 - 0.5) * Math.PI;
+    const lat1 = (v1 - 0.5) * Math.PI;
+
+    for (let across = 0; across < segmentsAround; ++across) {
+      const u0 = across / segmentsAround;
+      const u1 = (across + 1) / segmentsAround;
+      const long0 = u0 * Math.PI * 2;
+      const long1 = u1 * Math.PI * 2;
+
+      positions.set(getPoint(lat0, long0), posNdx);  posNdx += numComponents;
+      positions.set(getPoint(lat1, long0), posNdx);  posNdx += numComponents;
+      positions.set(getPoint(lat0, long1), posNdx);  posNdx += numComponents;
+      positions.set(getPoint(lat1, long1), posNdx);  posNdx += numComponents;
+
+      indices.push(
+        ndx, ndx + 1, ndx + 2,
+        ndx + 2, ndx + 1, ndx + 3,
+      );
+      ndx += 4;
+    }
+  }
+  return {positions, indices};
+}
+```
+
+We can then call it like this
+
+```js
+const segmentsAround = 24;
+const segmentsDown = 16;
+const {positions, indices} = makeSpherePositions(segmentsAround, segmentsDown);
+```
+
+Because positions returned are unit sphere positions so they are exactly the same
+values we need for normals so we can just duplicated them for the normals.
+
+```js
+const normals = positions.slice();
+```
+
+And then we setup the attributes like before
+
+```js
+const geometry = new THREE.BufferGeometry();
+const positionNumComponents = 3;
+const normalNumComponents = 3;
+
++const positionAttribute = new THREE.BufferAttribute(positions, positionNumComponents);
++positionAttribute.dynamic = true;
+geometry.addAttribute(
+    'position',
++    positionAttribute);
+geometry.addAttribute(
+    'normal',
+    new THREE.BufferAttribute(normals, normalNumComponents));
+geometry.setIndex(indices);
+```
+
+I've highlighted a few differences. We save a reference to the position attribute.
+We also mark it as dynamic. This is a hint to THREE.js that we're going to be changing
+the contents of the attribute often.
+
+In our render loop we update the positions based off their normals every frame.
+
+```js
+for (let i = 0; i < positions.length; i += 3) {
+  const quad = (i / 12 | 0);
+  const off1 = quad / segmentsAround | 0;
+  const off2 = quad % segmentsAround * Math.PI * 2 / segmentsAround;
+  temp.fromArray(normals, i);
+  temp.multiplyScalar(THREE.Math.lerp(1, 1.4, Math.sin(time + off1 + off2) * .5 + .5));
+  temp.toArray(positions, i);
+}
+positionAttribute.needsUpdate = true;
+```
+
+And we set `positionAttribute.needsUpdate` to tell THREE.js to use our changes.
+
+{{{example url="../threejs-custom-buffergeometry-dynamic.html"}}}
+
+I hope these were useful example of how to use `BufferGeometry` directly to
+make your own geometry and how to dynamically update the contents of a
+`BufferAttribute`. Which you use, `Geometry` or `BufferGeometry` really
+depends on your needs.
+
+<canvas id="c"></canvas>
+<script src="../resources/threejs/r103/three.min.js"></script>
+<script src="../resources/threejs/r103/js/controls/TrackballControls.js"></script>
+<script src="resources/threejs-lesson-utils.js"></script>
+<script src="resources/threejs-custom-buffergeometry.js"></script>
+
+

+ 1 - 1
threejs/lessons/threejs-custom-geometry.md

@@ -490,4 +490,4 @@ and we deleted the code related to spinning the cubes.
 I hope that was a useful instruction to making your own
 geometry using `Geometry`.
 
-In another article we'll go over `BufferGeometry`.
+In [another article](threejs-custom-buffergeometry.html) we'll go over `BufferGeometry`.

+ 1 - 0
threejs/lessons/toc.html

@@ -50,6 +50,7 @@
     <li><a href="/threejs/lessons/threejs-fog.html">Fog</a></li>
     <li><a href="/threejs/lessons/threejs-rendertargets.html">Render Targets</a></li>
     <li><a href="/threejs/lessons/threejs-custom-geometry.html">Custom Geometry</a></li>
+    <li><a href="/threejs/lessons/threejs-custom-buffergeometry.html">Custom BufferGeometry</a></li>
   </ul>
   <li>Reference</li>
   <ul>

BIN
threejs/resources/images/star-light.png


+ 172 - 0
threejs/threejs-custom-buffergeometry-cube-indexed.html

@@ -0,0 +1,172 @@
+<!-- Licensed under a BSD license. See license.html for license -->
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
+    <title>Three.js - Custom BufferGeometry - Indexed</title>
+    <style>
+    body {
+        margin: 0;
+    }
+    #c {
+        width: 100vw;
+        height: 100vh;
+        display: block;
+    }
+    </style>
+  </head>
+  <body>
+    <canvas id="c"></canvas>
+  </body>
+<script src="resources/threejs/r103/three.js"></script>
+<script>
+'use strict';
+
+/* global THREE */
+
+function main() {
+  const canvas = document.querySelector('#c');
+  const renderer = new THREE.WebGLRenderer({canvas});
+
+  const fov = 75;
+  const aspect = 2;  // the canvas default
+  const near = 0.1;
+  const far = 100;
+  const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
+  camera.position.z = 5;
+
+  const scene = new THREE.Scene();
+
+  {
+    const color = 0xFFFFFF;
+    const intensity = 1;
+    const light = new THREE.DirectionalLight(color, intensity);
+    light.position.set(-1, 2, 4);
+    scene.add(light);
+  }
+
+  // NOT A GOOD EXAMPLE OF HOW TO MAKE A CUBE!
+  // Only trying to make it clear most vertices are unique
+  const vertices = [
+    // front
+    { pos: [-1, -1,  1], norm: [ 0,  0,  1], uv: [0, 1], }, // 0
+    { pos: [ 1, -1,  1], norm: [ 0,  0,  1], uv: [1, 1], }, // 1
+    { pos: [-1,  1,  1], norm: [ 0,  0,  1], uv: [0, 0], }, // 2
+    { pos: [ 1,  1,  1], norm: [ 0,  0,  1], uv: [1, 0], }, // 3
+    // right
+    { pos: [ 1, -1,  1], norm: [ 1,  0,  0], uv: [0, 1], }, // 4
+    { pos: [ 1, -1, -1], norm: [ 1,  0,  0], uv: [1, 1], }, // 5
+    { pos: [ 1,  1,  1], norm: [ 1,  0,  0], uv: [0, 0], }, // 6
+    { pos: [ 1,  1, -1], norm: [ 1,  0,  0], uv: [1, 0], }, // 7
+    // back
+    { pos: [ 1, -1, -1], norm: [ 0,  0, -1], uv: [0, 1], }, // 8
+    { pos: [-1, -1, -1], norm: [ 0,  0, -1], uv: [1, 1], }, // 9
+    { pos: [ 1,  1, -1], norm: [ 0,  0, -1], uv: [0, 0], }, // 10
+    { pos: [-1,  1, -1], norm: [ 0,  0, -1], uv: [1, 0], }, // 11
+    // left
+    { pos: [-1, -1, -1], norm: [-1,  0,  0], uv: [0, 1], }, // 12
+    { pos: [-1, -1,  1], norm: [-1,  0,  0], uv: [1, 1], }, // 13
+    { pos: [-1,  1, -1], norm: [-1,  0,  0], uv: [0, 0], }, // 14
+    { pos: [-1,  1,  1], norm: [-1,  0,  0], uv: [1, 0], }, // 15
+    // top
+    { pos: [ 1,  1, -1], norm: [ 0,  1,  0], uv: [0, 1], }, // 16
+    { pos: [-1,  1, -1], norm: [ 0,  1,  0], uv: [1, 1], }, // 17
+    { pos: [ 1,  1,  1], norm: [ 0,  1,  0], uv: [0, 0], }, // 18
+    { pos: [-1,  1,  1], norm: [ 0,  1,  0], uv: [1, 0], }, // 19
+    // bottom
+    { pos: [ 1, -1,  1], norm: [ 0, -1,  0], uv: [0, 1], }, // 20
+    { pos: [-1, -1,  1], norm: [ 0, -1,  0], uv: [1, 1], }, // 21
+    { pos: [ 1, -1, -1], norm: [ 0, -1,  0], uv: [0, 0], }, // 22
+    { pos: [-1, -1, -1], norm: [ 0, -1,  0], uv: [1, 0], }, // 23
+  ];
+  const positions = [];
+  const normals = [];
+  const uvs = [];
+  for (const vertex of vertices) {
+    positions.push(...vertex.pos);
+    normals.push(...vertex.norm);
+    uvs.push(...vertex.uv);
+  }
+
+  const geometry = new THREE.BufferGeometry();
+  const positionNumComponents = 3;
+  const normalNumComponents = 3;
+  const uvNumComponents = 2;
+  geometry.addAttribute(
+      'position',
+      new THREE.BufferAttribute(new Float32Array(positions), positionNumComponents));
+  geometry.addAttribute(
+      'normal',
+      new THREE.BufferAttribute(new Float32Array(normals), normalNumComponents));
+  geometry.addAttribute(
+      'uv',
+      new THREE.BufferAttribute(new Float32Array(uvs), uvNumComponents));
+
+  geometry.setIndex([
+     0,  1,  2,   2,  1,  3,
+     4,  5,  6,   6,  5,  7,
+     8,  9, 10,  10,  9, 11,
+    12, 13, 14,  14, 13, 15,
+    16, 17, 18,  18, 17, 19,
+    20, 21, 22,  22, 21, 23,
+  ]);
+
+  const loader = new THREE.TextureLoader();
+  const texture = loader.load('resources/images/star.png');
+
+  function makeInstance(geometry, color, x) {
+    const material = new THREE.MeshPhongMaterial({color, map: texture});
+
+    const cube = new THREE.Mesh(geometry, material);
+    scene.add(cube);
+
+    cube.position.x = x;
+    return cube;
+  }
+
+  const cubes = [
+    makeInstance(geometry, 0x88FF88,  0),
+    makeInstance(geometry, 0x8888FF, -4),
+    makeInstance(geometry, 0xFF8888,  4),
+  ];
+
+  function resizeRendererToDisplaySize(renderer) {
+    const canvas = renderer.domElement;
+    const width = canvas.clientWidth;
+    const height = canvas.clientHeight;
+    const needResize = canvas.width !== width || canvas.height !== height;
+    if (needResize) {
+      renderer.setSize(width, height, false);
+    }
+    return needResize;
+  }
+
+  function render(time) {
+    time *= 0.001;
+
+    if (resizeRendererToDisplaySize(renderer)) {
+      const canvas = renderer.domElement;
+      camera.aspect = canvas.clientWidth / canvas.clientHeight;
+      camera.updateProjectionMatrix();
+    }
+
+    cubes.forEach((cube, ndx) => {
+      const speed = 1 + ndx * .1;
+      const rot = time * speed;
+      cube.rotation.x = rot;
+      cube.rotation.y = rot;
+    });
+
+    renderer.render(scene, camera);
+
+    requestAnimationFrame(render);
+  }
+
+  requestAnimationFrame(render);
+}
+
+main();
+</script>
+</html>
+

+ 179 - 0
threejs/threejs-custom-buffergeometry-cube-typedarrays.html

@@ -0,0 +1,179 @@
+<!-- Licensed under a BSD license. See license.html for license -->
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
+    <title>Three.js - Custom BufferGeometry - TypedArrays</title>
+    <style>
+    body {
+        margin: 0;
+    }
+    #c {
+        width: 100vw;
+        height: 100vh;
+        display: block;
+    }
+    </style>
+  </head>
+  <body>
+    <canvas id="c"></canvas>
+  </body>
+<script src="resources/threejs/r103/three.js"></script>
+<script>
+'use strict';
+
+/* global THREE */
+
+function main() {
+  const canvas = document.querySelector('#c');
+  const renderer = new THREE.WebGLRenderer({canvas});
+
+  const fov = 75;
+  const aspect = 2;  // the canvas default
+  const near = 0.1;
+  const far = 100;
+  const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
+  camera.position.z = 5;
+
+  const scene = new THREE.Scene();
+
+  {
+    const color = 0xFFFFFF;
+    const intensity = 1;
+    const light = new THREE.DirectionalLight(color, intensity);
+    light.position.set(-1, 2, 4);
+    scene.add(light);
+  }
+
+  // NOT A GOOD EXAMPLE OF HOW TO MAKE A CUBE!
+  // Only trying to make it clear most vertices are unique
+  const vertices = [
+    // front
+    { pos: [-1, -1,  1], norm: [ 0,  0,  1], uv: [0, 1], }, // 0
+    { pos: [ 1, -1,  1], norm: [ 0,  0,  1], uv: [1, 1], }, // 1
+    { pos: [-1,  1,  1], norm: [ 0,  0,  1], uv: [0, 0], }, // 2
+    { pos: [ 1,  1,  1], norm: [ 0,  0,  1], uv: [1, 0], }, // 3
+    // right
+    { pos: [ 1, -1,  1], norm: [ 1,  0,  0], uv: [0, 1], }, // 4
+    { pos: [ 1, -1, -1], norm: [ 1,  0,  0], uv: [1, 1], }, // 5
+    { pos: [ 1,  1,  1], norm: [ 1,  0,  0], uv: [0, 0], }, // 6
+    { pos: [ 1,  1, -1], norm: [ 1,  0,  0], uv: [1, 0], }, // 7
+    // back
+    { pos: [ 1, -1, -1], norm: [ 0,  0, -1], uv: [0, 1], }, // 8
+    { pos: [-1, -1, -1], norm: [ 0,  0, -1], uv: [1, 1], }, // 9
+    { pos: [ 1,  1, -1], norm: [ 0,  0, -1], uv: [0, 0], }, // 10
+    { pos: [-1,  1, -1], norm: [ 0,  0, -1], uv: [1, 0], }, // 11
+    // left
+    { pos: [-1, -1, -1], norm: [-1,  0,  0], uv: [0, 1], }, // 12
+    { pos: [-1, -1,  1], norm: [-1,  0,  0], uv: [1, 1], }, // 13
+    { pos: [-1,  1, -1], norm: [-1,  0,  0], uv: [0, 0], }, // 14
+    { pos: [-1,  1,  1], norm: [-1,  0,  0], uv: [1, 0], }, // 15
+    // top
+    { pos: [ 1,  1, -1], norm: [ 0,  1,  0], uv: [0, 1], }, // 16
+    { pos: [-1,  1, -1], norm: [ 0,  1,  0], uv: [1, 1], }, // 17
+    { pos: [ 1,  1,  1], norm: [ 0,  1,  0], uv: [0, 0], }, // 18
+    { pos: [-1,  1,  1], norm: [ 0,  1,  0], uv: [1, 0], }, // 19
+    // bottom
+    { pos: [ 1, -1,  1], norm: [ 0, -1,  0], uv: [0, 1], }, // 20
+    { pos: [-1, -1,  1], norm: [ 0, -1,  0], uv: [1, 1], }, // 21
+    { pos: [ 1, -1, -1], norm: [ 0, -1,  0], uv: [0, 0], }, // 22
+    { pos: [-1, -1, -1], norm: [ 0, -1,  0], uv: [1, 0], }, // 23
+  ];
+  const numVertices = vertices.length;
+  const positionNumComponents = 3;
+  const normalNumComponents = 3;
+  const uvNumComponents = 2;
+  const positions = new Float32Array(numVertices * positionNumComponents);
+  const normals = new Float32Array(numVertices * normalNumComponents);
+  const uvs = new Float32Array(numVertices * uvNumComponents);
+  let posNdx = 0;
+  let nrmNdx = 0;
+  let uvNdx = 0;
+  for (const vertex of vertices) {
+    positions.set(vertex.pos, posNdx);
+    normals.set(vertex.norm, nrmNdx);
+    uvs.set(vertex.uv, uvNdx);
+    posNdx += positionNumComponents;
+    nrmNdx += normalNumComponents;
+    uvNdx += uvNumComponents;
+  }
+
+  const geometry = new THREE.BufferGeometry();
+  geometry.addAttribute(
+      'position',
+      new THREE.BufferAttribute(positions, positionNumComponents));
+  geometry.addAttribute(
+      'normal',
+      new THREE.BufferAttribute(normals, normalNumComponents));
+  geometry.addAttribute(
+      'uv',
+      new THREE.BufferAttribute(uvs, uvNumComponents));
+
+  geometry.setIndex([
+     0,  1,  2,   2,  1,  3,  // front
+     4,  5,  6,   6,  5,  7,  // right
+     8,  9, 10,  10,  9, 11,  // back
+    12, 13, 14,  14, 13, 15,  // left
+    16, 17, 18,  18, 17, 19,  // top
+    20, 21, 22,  22, 21, 23,  // bottom
+  ]);
+
+  const loader = new THREE.TextureLoader();
+  const texture = loader.load('resources/images/star.png');
+
+  function makeInstance(geometry, color, x) {
+    const material = new THREE.MeshPhongMaterial({color, map: texture});
+
+    const cube = new THREE.Mesh(geometry, material);
+    scene.add(cube);
+
+    cube.position.x = x;
+    return cube;
+  }
+
+  const cubes = [
+    makeInstance(geometry, 0x88FF88,  0),
+    makeInstance(geometry, 0x8888FF, -4),
+    makeInstance(geometry, 0xFF8888,  4),
+  ];
+
+  function resizeRendererToDisplaySize(renderer) {
+    const canvas = renderer.domElement;
+    const width = canvas.clientWidth;
+    const height = canvas.clientHeight;
+    const needResize = canvas.width !== width || canvas.height !== height;
+    if (needResize) {
+      renderer.setSize(width, height, false);
+    }
+    return needResize;
+  }
+
+  function render(time) {
+    time *= 0.001;
+
+    if (resizeRendererToDisplaySize(renderer)) {
+      const canvas = renderer.domElement;
+      camera.aspect = canvas.clientWidth / canvas.clientHeight;
+      camera.updateProjectionMatrix();
+    }
+
+    cubes.forEach((cube, ndx) => {
+      const speed = 1 + ndx * .1;
+      const rot = time * speed;
+      cube.rotation.x = rot;
+      cube.rotation.y = rot;
+    });
+
+    renderer.render(scene, camera);
+
+    requestAnimationFrame(render);
+  }
+
+  requestAnimationFrame(render);
+}
+
+main();
+</script>
+</html>
+

+ 181 - 0
threejs/threejs-custom-buffergeometry-cube.html

@@ -0,0 +1,181 @@
+<!-- Licensed under a BSD license. See license.html for license -->
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
+    <title>Three.js - Custom BufferGeometry - Cube</title>
+    <style>
+    body {
+        margin: 0;
+    }
+    #c {
+        width: 100vw;
+        height: 100vh;
+        display: block;
+    }
+    </style>
+  </head>
+  <body>
+    <canvas id="c"></canvas>
+  </body>
+<script src="resources/threejs/r103/three.js"></script>
+<script>
+'use strict';
+
+/* global THREE */
+
+function main() {
+  const canvas = document.querySelector('#c');
+  const renderer = new THREE.WebGLRenderer({canvas});
+
+  const fov = 75;
+  const aspect = 2;  // the canvas default
+  const near = 0.1;
+  const far = 100;
+  const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
+  camera.position.z = 5;
+
+  const scene = new THREE.Scene();
+
+  {
+    const color = 0xFFFFFF;
+    const intensity = 1;
+    const light = new THREE.DirectionalLight(color, intensity);
+    light.position.set(-1, 2, 4);
+    scene.add(light);
+  }
+
+  // NOT A GOOD EXAMPLE OF HOW TO MAKE A CUBE!
+  // Only trying to make it clear most vertices are unique
+  const vertices = [
+    // front
+    { pos: [-1, -1,  1], norm: [ 0,  0,  1], uv: [0, 1], },
+    { pos: [ 1, -1,  1], norm: [ 0,  0,  1], uv: [1, 1], },
+    { pos: [-1,  1,  1], norm: [ 0,  0,  1], uv: [0, 0], },
+
+    { pos: [-1,  1,  1], norm: [ 0,  0,  1], uv: [0, 0], },
+    { pos: [ 1, -1,  1], norm: [ 0,  0,  1], uv: [1, 1], },
+    { pos: [ 1,  1,  1], norm: [ 0,  0,  1], uv: [1, 0], },
+    // right
+    { pos: [ 1, -1,  1], norm: [ 1,  0,  0], uv: [0, 1], },
+    { pos: [ 1, -1, -1], norm: [ 1,  0,  0], uv: [1, 1], },
+    { pos: [ 1,  1,  1], norm: [ 1,  0,  0], uv: [0, 0], },
+
+    { pos: [ 1,  1,  1], norm: [ 1,  0,  0], uv: [0, 0], },
+    { pos: [ 1, -1, -1], norm: [ 1,  0,  0], uv: [1, 1], },
+    { pos: [ 1,  1, -1], norm: [ 1,  0,  0], uv: [1, 0], },
+    // back
+    { pos: [ 1, -1, -1], norm: [ 0,  0, -1], uv: [0, 1], },
+    { pos: [-1, -1, -1], norm: [ 0,  0, -1], uv: [1, 1], },
+    { pos: [ 1,  1, -1], norm: [ 0,  0, -1], uv: [0, 0], },
+
+    { pos: [ 1,  1, -1], norm: [ 0,  0, -1], uv: [0, 0], },
+    { pos: [-1, -1, -1], norm: [ 0,  0, -1], uv: [1, 1], },
+    { pos: [-1,  1, -1], norm: [ 0,  0, -1], uv: [1, 0], },
+    // left
+    { pos: [-1, -1, -1], norm: [-1,  0,  0], uv: [0, 1], },
+    { pos: [-1, -1,  1], norm: [-1,  0,  0], uv: [1, 1], },
+    { pos: [-1,  1, -1], norm: [-1,  0,  0], uv: [0, 0], },
+
+    { pos: [-1,  1, -1], norm: [-1,  0,  0], uv: [0, 0], },
+    { pos: [-1, -1,  1], norm: [-1,  0,  0], uv: [1, 1], },
+    { pos: [-1,  1,  1], norm: [-1,  0,  0], uv: [1, 0], },
+    // top
+    { pos: [ 1,  1, -1], norm: [ 0,  1,  0], uv: [0, 1], },
+    { pos: [-1,  1, -1], norm: [ 0,  1,  0], uv: [1, 1], },
+    { pos: [ 1,  1,  1], norm: [ 0,  1,  0], uv: [0, 0], },
+
+    { pos: [ 1,  1,  1], norm: [ 0,  1,  0], uv: [0, 0], },
+    { pos: [-1,  1, -1], norm: [ 0,  1,  0], uv: [1, 1], },
+    { pos: [-1,  1,  1], norm: [ 0,  1,  0], uv: [1, 0], },
+    // bottom
+    { pos: [ 1, -1,  1], norm: [ 0, -1,  0], uv: [0, 1], },
+    { pos: [-1, -1,  1], norm: [ 0, -1,  0], uv: [1, 1], },
+    { pos: [ 1, -1, -1], norm: [ 0, -1,  0], uv: [0, 0], },
+
+    { pos: [ 1, -1, -1], norm: [ 0, -1,  0], uv: [0, 0], },
+    { pos: [-1, -1,  1], norm: [ 0, -1,  0], uv: [1, 1], },
+    { pos: [-1, -1, -1], norm: [ 0, -1,  0], uv: [1, 0], },
+  ];
+  const positions = [];
+  const normals = [];
+  const uvs = [];
+  for (const vertex of vertices) {
+    positions.push(...vertex.pos);
+    normals.push(...vertex.norm);
+    uvs.push(...vertex.uv);
+  }
+
+  const geometry = new THREE.BufferGeometry();
+  const positionNumComponents = 3;
+  const normalNumComponents = 3;
+  const uvNumComponents = 2;
+  geometry.addAttribute(
+      'position',
+      new THREE.BufferAttribute(new Float32Array(positions), positionNumComponents));
+  geometry.addAttribute(
+      'normal',
+      new THREE.BufferAttribute(new Float32Array(normals), normalNumComponents));
+  geometry.addAttribute(
+      'uv',
+      new THREE.BufferAttribute(new Float32Array(uvs), uvNumComponents));
+
+  const loader = new THREE.TextureLoader();
+  const texture = loader.load('resources/images/star.png');
+
+  function makeInstance(geometry, color, x) {
+    const material = new THREE.MeshPhongMaterial({color, map: texture});
+
+    const cube = new THREE.Mesh(geometry, material);
+    scene.add(cube);
+
+    cube.position.x = x;
+    return cube;
+  }
+
+  const cubes = [
+    makeInstance(geometry, 0x88FF88,  0),
+    makeInstance(geometry, 0x8888FF, -4),
+    makeInstance(geometry, 0xFF8888,  4),
+  ];
+
+  function resizeRendererToDisplaySize(renderer) {
+    const canvas = renderer.domElement;
+    const width = canvas.clientWidth;
+    const height = canvas.clientHeight;
+    const needResize = canvas.width !== width || canvas.height !== height;
+    if (needResize) {
+      renderer.setSize(width, height, false);
+    }
+    return needResize;
+  }
+
+  function render(time) {
+    time *= 0.001;
+
+    if (resizeRendererToDisplaySize(renderer)) {
+      const canvas = renderer.domElement;
+      camera.aspect = canvas.clientWidth / canvas.clientHeight;
+      camera.updateProjectionMatrix();
+    }
+
+    cubes.forEach((cube, ndx) => {
+      const speed = 1 + ndx * .1;
+      const rot = time * speed;
+      cube.rotation.x = rot;
+      cube.rotation.y = rot;
+    });
+
+    renderer.render(scene, camera);
+
+    requestAnimationFrame(render);
+  }
+
+  requestAnimationFrame(render);
+}
+
+main();
+</script>
+</html>
+

+ 188 - 0
threejs/threejs-custom-buffergeometry-dynamic.html

@@ -0,0 +1,188 @@
+<!-- Licensed under a BSD license. See license.html for license -->
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
+    <title>Three.js - Custom BufferGeometry - Dynamic</title>
+    <style>
+    body {
+        margin: 0;
+    }
+    #c {
+        width: 100vw;
+        height: 100vh;
+        display: block;
+    }
+    </style>
+  </head>
+  <body>
+    <canvas id="c"></canvas>
+  </body>
+<script src="resources/threejs/r103/three.js"></script>
+<script>
+'use strict';
+
+/* global THREE */
+
+function main() {
+  const canvas = document.querySelector('#c');
+  const renderer = new THREE.WebGLRenderer({canvas});
+
+  const fov = 75;
+  const aspect = 2;  // the canvas default
+  const near = 0.1;
+  const far = 100;
+  const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
+  camera.position.z = 3;
+
+  const scene = new THREE.Scene();
+
+  function addLight(...pos) {
+    const color = 0xFFFFFF;
+    const intensity = 1;
+    const light = new THREE.DirectionalLight(color, intensity);
+    light.position.set(...pos);
+    scene.add(light);
+  }
+  addLight(-1, 2, 4);
+  addLight( 2, -2, 3);
+
+  function makeSpherePositions(segmentsAround, segmentsDown) {
+    const numVertices = segmentsAround * segmentsDown * 6;
+    const numComponents = 3;
+    const positions = new Float32Array(numVertices * numComponents);
+    const indices = [];
+
+    const longHelper = new THREE.Object3D();
+    const latHelper = new THREE.Object3D();
+    const pointHelper = new THREE.Object3D();
+    longHelper.add(latHelper);
+    latHelper.add(pointHelper);
+    pointHelper.position.z = 1;
+    const temp = new THREE.Vector3();
+
+    function getPoint(lat, long) {
+      latHelper.rotation.x = lat;
+      longHelper.rotation.y = long;
+      longHelper.updateMatrixWorld(true);
+      return pointHelper.getWorldPosition(temp).toArray();
+    }
+
+    let posNdx = 0;
+    let ndx = 0;
+    for (let down = 0; down < segmentsDown; ++down) {
+      const v0 = down / segmentsDown;
+      const v1 = (down + 1) / segmentsDown;
+      const lat0 = (v0 - 0.5) * Math.PI;
+      const lat1 = (v1 - 0.5) * Math.PI;
+
+      for (let across = 0; across < segmentsAround; ++across) {
+        const u0 = across / segmentsAround;
+        const u1 = (across + 1) / segmentsAround;
+        const long0 = u0 * Math.PI * 2;
+        const long1 = u1 * Math.PI * 2;
+
+        positions.set(getPoint(lat0, long0), posNdx);  posNdx += numComponents;
+        positions.set(getPoint(lat1, long0), posNdx);  posNdx += numComponents;
+        positions.set(getPoint(lat0, long1), posNdx);  posNdx += numComponents;
+        positions.set(getPoint(lat1, long1), posNdx);  posNdx += numComponents;
+
+        indices.push(
+          ndx, ndx + 1, ndx + 2,
+          ndx + 2, ndx + 1, ndx + 3,
+        );
+        ndx += 4;
+      }
+    }
+    return {positions, indices};
+  }
+
+  const segmentsAround = 24;
+  const segmentsDown = 16;
+  const {positions, indices} = makeSpherePositions(segmentsAround, segmentsDown);
+
+  const normals = positions.slice();
+
+  const geometry = new THREE.BufferGeometry();
+  const positionNumComponents = 3;
+  const normalNumComponents = 3;
+
+  const positionAttribute = new THREE.BufferAttribute(positions, positionNumComponents);
+  positionAttribute.dynamic = true;
+  geometry.addAttribute(
+      'position',
+      positionAttribute);
+  geometry.addAttribute(
+      'normal',
+      new THREE.BufferAttribute(normals, normalNumComponents));
+  geometry.setIndex(indices);
+
+  function makeInstance(geometry, color, x) {
+    const material = new THREE.MeshPhongMaterial({
+      color,
+      side: THREE.DoubleSide,
+      shininess: 100,
+    });
+
+    const cube = new THREE.Mesh(geometry, material);
+    scene.add(cube);
+
+    cube.position.x = x;
+    return cube;
+  }
+
+  const cubes = [
+    makeInstance(geometry, 0xFF0000, 0),
+  ];
+
+  function resizeRendererToDisplaySize(renderer) {
+    const canvas = renderer.domElement;
+    const width = canvas.clientWidth;
+    const height = canvas.clientHeight;
+    const needResize = canvas.width !== width || canvas.height !== height;
+    if (needResize) {
+      renderer.setSize(width, height, false);
+    }
+    return needResize;
+  }
+
+  const temp = new THREE.Vector3();
+
+  function render(time) {
+    time *= 0.001;
+
+    if (resizeRendererToDisplaySize(renderer)) {
+      const canvas = renderer.domElement;
+      camera.aspect = canvas.clientWidth / canvas.clientHeight;
+      camera.updateProjectionMatrix();
+    }
+
+    for (let i = 0; i < positions.length; i += 3) {
+      const quad = (i / 12 | 0);
+      const off1 = quad / segmentsAround | 0;
+      const off2 = quad % segmentsAround * Math.PI * 2 / segmentsAround;
+      temp.fromArray(normals, i);
+      temp.multiplyScalar(THREE.Math.lerp(1, 1.4, Math.sin(time + off1 + off2) * .5 + .5));
+      temp.toArray(positions, i);
+    }
+    positionAttribute.needsUpdate = true;
+
+    cubes.forEach((cube, ndx) => {
+      const speed = -0.2 + ndx * .1;
+      const rot = time * speed;
+      cube.rotation.y = rot;
+    });
+
+    renderer.render(scene, camera);
+
+    requestAnimationFrame(render);
+  }
+
+  requestAnimationFrame(render);
+}
+
+main();
+</script>
+</html>
+