/* Copyright (c) 2011 EL-EMENT saharan * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * software and associated documentation * files (the "Software"), to deal in the Software * without restriction, including without limitation the rights to use, copy, * modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to * whom the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies or * substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package { import com.adobe.utils.*; import flash.display.*; import flash.display3D.*; import flash.events.*; import flash.geom.*; [SWF(width = "480", height = "480", frameRate = "60")] public class Cloth extends Sprite { private var gl:EGraphics; private var cloth:EMesh; private var plane:EMesh; private var ball:EMesh; private var s1:EShader; private var s2:EShader; private var rx:Number; private var ry:Number; private var rvx:Number; private var rvy:Number; private var press:Boolean; private var pmouseX:Number; private var pmouseY:Number; private var vs:Vector.; private var ss:Vector.; private var bs:Vector.; private var cs:Vector.; private var numv:uint; private var nums:uint; private var numc:uint; public function Cloth() { if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } private function init(e:Event = null): void { removeEventListener(Event.ADDED_TO_STAGE, init); stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; stage.addEventListener(MouseEvent.MOUSE_DOWN, function(e:Event):void { press = true; }); stage.addEventListener(MouseEvent.MOUSE_UP, function(e:Event):void { press = false; }); rx = 0; ry = 0; rvx = 0; rvy = 0; pmouseX = 0; pmouseY = 0; vs = new Vector.(); ss = new Vector.(); bs = new Vector.(); cs = new Vector.(); // gl = new EGraphics(stage.stage3Ds[0], 480, 480, start); } public function createBall(divV:uint, divH:uint, radius:Number):EMesh { // 球メッシュの作成 var mesh:EMesh = new EMesh(); var theta:Number; var phi:Number; var dTheta:Number = Math.PI * 2 / divV; var dPhi:Number = Math.PI / divH; var numVertices:uint = (divH + 1) * divV - (divV - 1) * 2; mesh.addVertex(0, radius, 0); phi = dPhi; for (var i:int = 1; i < divH; i++) { theta = Math.PI * 2; for (var j:int = 0; j < divV; j++) { var index:int = (i - 1) * divV + j + 1; mesh.addVertex(radius * Math.sin(phi) * Math.cos(theta), radius * Math.cos(phi), radius * Math.sin(phi) * Math.sin(theta), j / divV, i / divH); theta -= dTheta; } phi += dPhi; } mesh.addVertex(0, -radius, 0); for (i = 0; i < divH; i++) { for (j = 0; j < divV; j++) { if (i == 0) { mesh.addFace(0, (j + 1) % divV + 1, j + 1); } else if (i == divH - 1) { mesh.addFace(numVertices - 1, (i - 1) * divV + j + 1, (i - 1) * divV + (j + 1) % divV + 1); } else { mesh.addFace((i - 1) * divV + j + 1, (i - 1) * divV + (j + 1) % divV + 1, i * divV + (j + 1) % divV + 1); mesh.addFace((i - 1) * divV + j + 1, i * divV + (j + 1) % divV + 1, i * divV + j + 1); } } } return mesh; } public function createClothMesh(divV:uint, divH:uint, size:Number):EMesh { // 布メッシュの作成 var mesh:EMesh = new EMesh(); var x:Number = -size * 0.5; var y:Number = size * 0.5; var dx:Number = size / divV; var dy:Number = -size / divH; for (var j:int = 0; j <= divH; j++) { x = -size * 0.5; for (var i:int = 0; i <= divV; i++) { mesh.addVertex(x, y, 0, i / divV, j / divH); x += dx; } y += dy; } for (i = 0; i < divH; i++) { for (j = 0; j < divV; j++) { mesh.addFace(i * (divV + 1) + j, i * (divV + 1) + j + 1, (i + 1) * (divV + 1) + j); mesh.addFace(i * (divV + 1) + j + 1, (i + 1) * (divV + 1) + j + 1, (i + 1) * (divV + 1) + j); } } return mesh; } private function start():void { s1 = new EShader(vertexShaderCode, fragmentShaderCode, gl.context3D); s2 = new EShader(vertexShaderCode, fragmentShaderCodeBall, gl.context3D); var detail:uint = 24; var size:uint = 250; var x:Number; var y:Number = 0; var d:Number = size / detail; numv = 0; numc = 0; for (var j:int = 0; j <= detail; j++) { x = -size * 0.5; for (var i:int = 0; i <= detail; i++) { var tv:Vector3D = new Vector3D(x, size * 0.7, y); vs[numv++] = new Vertex(tv.x, tv.y, tv.z, j == 0 && (i % 12 == 0)); x += d; } y -= d; } nums = 0; for (j = 0; j <= detail; j++) { for (i = 0; i <= detail; i++) { var idx:int = j * (detail + 1) + i; if (i < detail) ss[nums++] = new Spring(vs[idx], vs[idx + 1], 0.6); if (j < detail) ss[nums++] = new Spring(vs[idx], vs[idx + detail + 1], 0.6); if (i < detail - 1) ss[nums++] = new Spring(vs[idx], vs[idx + 2], 0.3); if (j < detail - 1) ss[nums++] = new Spring(vs[idx], vs[idx + (detail + 1) * 2], 0.3); if (i < detail && j < detail) ss[nums++] = new Spring(vs[idx], vs[idx + detail + 2], 0.5); } } ball = createBall(16, 8, 1); ball.initVertexBuffer(gl); ball.initIndexBuffer(gl); ball.calcNormals(); cloth = createClothMesh(detail, detail, size); cloth.initVertexBuffer(gl); cloth.initIndexBuffer(gl); plane = new EMesh(); plane.addVertex(-200, -102, -200, 0, 0, 0.8, 0.8, 0.8); plane.addVertex(200, -102, -200, 0, 0, 0.8, 0.8, 0.8); plane.addVertex(200, -102, 200, 0, 0, 0.8, 0.8, 0.8); plane.addVertex(-200, -102, 200, 0, 0, 0.8, 0.8, 0.8); plane.addFace(0, 1, 2); plane.addFace(0, 2, 3); plane.initVertexBuffer(gl); plane.initIndexBuffer(gl); plane.calcNormals(); var prj:PerspectiveMatrix3D = new PerspectiveMatrix3D(); prj.perspectiveFieldOfViewRH(60 * Math.PI / 180, 1, 0.01, 2000); gl.context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 4, prj, true); stage.addEventListener(MouseEvent.MOUSE_DOWN, function(e:Event = null):void { var ang:Number = (ry - 90) * Math.PI / 180 + Math.random() * 0.5 - 0.25; // 球体を発射 var sin:Number = Math.sin(ang); var cos:Number = Math.cos(ang); var ang2:Number = ang + Math.random() * 0.25 - 0.125; var sin2:Number = Math.sin(ang2); var cos2:Number = Math.cos(ang2); bs.push(new Ball(-cos * 200, 50, -sin * 200, cos2 * 12, Math.random() * 3, sin2 * 12)); } ); addEventListener(Event.ENTER_FRAME, frame); } private function frame(e:Event = null): void { if (press) { rvx += (mouseY - pmouseY) * 0.0625; rvy += (mouseX - pmouseX) * 0.0625; } pmouseX = mouseX; pmouseY = mouseY; rvx *= 0.9; rvy *= 0.9; rx += rvx; ry += rvy; if (rx > 90) rx += (90 - rx) * 0.5; if (rx < -90) rx += (-90 - rx) * 0.5; // simulate(); var vts:Vector. = cloth.vertices; for (var i:int = EGraphics.OFFSET_VERTEX, j:int = 0; i < vts.length; i += EGraphics.NUM_DATAS_IN_VERTEX, j++) { vts[i] = vs[j].x; vts[i + 1] = vs[j].y; vts[i + 2] = vs[j].z; } cloth.calcNormals(); // var mdl:Matrix3D = new Matrix3D(); mdl.prependTranslation(0, 0, -400); mdl.prependRotation(rx, Vector3D.X_AXIS); mdl.prependRotation(ry, Vector3D.Y_AXIS); var lightVec:Vector3D = new Vector3D(0, -1, -2); lightVec.normalize(); gl.context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, Vector.([lightVec.x, lightVec.y, lightVec.z, 1])); // gl.beginScene(0, 0, 0); gl.setShader(s1); gl.context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, mdl, true); gl.context3D.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 8, Vector.([0.8, 0.2, 0.2, 1])); gl.context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 11, Vector.([-1, 0, 0, 1])); gl.context3D.setCulling(Context3DTriangleFace.FRONT); gl.renderMesh(cloth); // 裏面を描画 gl.context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 11, Vector.([1, 0, 0, 1])); gl.context3D.setCulling(Context3DTriangleFace.BACK); gl.renderMesh(cloth); // 表面を描画 // gl.setShader(s2); gl.context3D.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 8, Vector.([0.8, 0.8, 0.8, 1])); gl.renderMesh(plane); var numb:uint = bs.length; for (i = 0; i < numb; i++) { var mtx:Matrix3D = mdl.clone(); mtx.prependTranslation(bs[i].x, bs[i].y, bs[i].z); mtx.prependScale(bs[i].radius - 3, bs[i].radius - 3, bs[i].radius - 3); gl.context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, mtx, true); gl.context3D.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 8, bs[i].color); gl.renderMesh(ball); } gl.endScene(); } private function simulate():void { var i:int; var j:int; while (numc > 0) cs[--numc] = null; // for GC var numb:uint = bs.length; for (j = 0; j < numb; j++) { // 簡易衝突判定 (n^2) var c:Contact; for (i = 0; i < numv; i++) { c = vs[i].collide(bs[j]); // 球と布の接触 if (c) cs[numc++] = c; } for (i = 0; i < j; i++) { c = bs[i].collide(bs[j]); // 球同士の接触 if (c) cs[numc++] = c; } } for (j = 0; j < 4; j++) { // 連立方程式を解く for (i = 0; i < nums; i++) ss[i].solve(); for (i = 0; i < numc; i++) cs[i].solve(); for (i = 0; i < numv; i++) { // 床との接触 var v:Vertex = vs[i]; if (v.y < -100) if (v.vy < 0) v.vy = 0; } for (i = 0; i < numb; i++) { // 床との接触 var b:Ball = bs[i]; if (b.x > -200 && b.x < 200 && b.z > -200 && b.z < 200 && b.y <= -100 + b.radius && b.vy < 0) b.vy *= -0.4; } } for (i = 0; i < numb; i++) { bs[i].move(); if (bs[i].y < -300) bs.splice(i--, 1), numb--; // 落下判定 } for (i = 0; i < numv; i++) vs[i].move(); } } } class Vertex { // 質点 public var x:Number; public var y:Number; public var z:Number; public var vx:Number; public var vy:Number; public var vz:Number; public var fix:Boolean; public var mass:Number; // 質量の逆数 public function Vertex(x:Number, y:Number, z:Number, fix:Boolean) { this.x = x; this.y = y; this.z = z; vx = Math.random() * 0.01 - 0.005; vy = Math.random() * 0.01 - 0.005; vz = Math.random() * 0.01 - 0.005; this.fix = fix; mass = fix ? 0 : 1; // 固定なら質量無限 (逆数0) } public function move():void { if (fix) vx = vy = vz = 0; else { x += vx; y += vy; if (y < -100) y = -100; z += vz; vy -= 0.15; } } public function collide(b:Ball):Contact { var dx:Number = x - b.x; if (dx < -b.radius || dx > b.radius) return null; var dy:Number = y - b.y; if (dy < -b.radius || dy > b.radius) return null; var dz:Number = z - b.z; if (dz < -b.radius || dz > b.radius) return null; var d2:Number = dx * dx + dy * dy + dz * dz; if (d2 < b.radius * b.radius) { var d:Number = 1 / Math.sqrt(d2); dx *= d; dy *= d; dz *= d; return new Contact(this, b, null, dx, dy, dz, 0); } return null; } } class Spring { // ばね public var v1:Vertex; public var v2:Vertex; private var rest:Number; // 休息距離 private var strength:Number; private var mass:Number; public function Spring(v1:Vertex, v2:Vertex, str:Number) { this.v1 = v1; this.v2 = v2; this.strength = str; mass = 1 / (v1.mass + v2.mass); // 適正質量 rest = Math.sqrt((v2.x - v1.x) * (v2.x - v1.x) + (v2.y - v1.y) * (v2.y - v1.y) + (v2.z - v1.z) * (v2.z - v1.z)); } public function solve():void { // ばねの計算 if (v1.mass + v2.mass == 0) return; var dx:Number = v2.x - v1.x + v2.vx - v1.vx; // t+1 時でのばね係数を計算 var dy:Number = v2.y - v1.y + v2.vy - v1.vy; var dz:Number = v2.z - v1.z + v2.vz - v1.vz; var dist:Number = Math.sqrt(dx * dx + dy * dy + dz * dz); var fs:Number = (dist - rest) * strength; dist = 1 / dist; var nx:Number = dx * dist; var ny:Number = dy * dist; var nz:Number = dz * dist; var fd:Number = ((v2.vx - v1.vx) * nx + (v2.vy - v1.vy) * ny + (v2.vz - v1.vz) * nz) * 0.4 * strength; var f:Number = (fs + fd) * mass; var fx:Number = nx * f; var fy:Number = ny * f; var fz:Number = nz * f; v1.vx += fx * v1.mass; v1.vy += fy * v1.mass; v1.vz += fz * v1.mass; v2.vx -= fx * v2.mass; v2.vy -= fy * v2.mass; v2.vz -= fz * v2.mass; } } class Contact { // 接触点 public var v:Vertex; public var b1:Ball; public var b2:Ball; private var btb:Boolean; private var nx:Number; private var ny:Number; private var nz:Number; private var tv:Number; private var f:Number; private var mass:Number; public function Contact(v:Vertex, b1:Ball, b2:Ball, nx:Number, ny:Number, nz:Number, tv:Number) { this.v = v; btb = !v; this.b1 = b1; this.b2 = b2; this.nx = nx; this.ny = ny; this.nz = nz; this.tv = tv; // 目標とする相対速度 (eを反発係数として -e * 法線方向の相対速度) if (btb) mass = 1 / (b1.mass + b2.mass); // 適正質量 else mass = 1 / (v.mass + b1.mass); f = 0; // Warm Startを使う場合はこの値を前回のループの値に設定する } public function solve():void { // めり込まない拘束条件 : 法線方向の相対速度 >= 0 var r:Number; var fce:Number; var nf:Number; var sub:Number; var fx:Number; var fy:Number; var fz:Number; if (btb) { // ball vs ball r = (b2.vx - b1.vx) * nx + (b2.vy - b1.vy) * ny + (b2.vz - b1.vz) * nz; // 相対速度 fce = mass * (r - tv); // 適正質量を掛ける nf = fce + f; if (nf < 0) nf = 0; // 離れる方向へは力を適用しない sub = nf - f; // 前回の解との差分を適用 fx = nx * sub; fy = ny * sub; fz = nz * sub; b1.vx += fx * b1.mass; b1.vy += fy * b1.mass; b1.vz += fz * b1.mass; b2.vx -= fx * b2.mass; b2.vy -= fy * b2.mass; b2.vz -= fz * b2.mass; f = nf; // 力を蓄積 (この方法は垂直効力や摩擦力など解が非線形である場合のみ有効) } else { // vertex vs ball r = (b1.vx - v.vx) * nx + (b1.vy - v.vy) * ny + (b1.vz - v.vz) * nz; // 相対速度 fce = mass * (r - tv); // 適正質量を掛ける nf = fce + f; if (nf < 0) nf = 0; // 離れる方向へは力を適用しない sub = nf - f; // 前回の解との差分を適用 fx = nx * sub; fy = ny * sub; fz = nz * sub; v.vx += fx * v.mass; v.vy += fy * v.mass; v.vz += fz * v.mass; b1.vx -= fx * b1.mass; b1.vy -= fy * b1.mass; b1.vz -= fz * b1.mass; f = nf; // 力を蓄積 (この方法は垂直効力や摩擦力など解が非線形である場合のみ有効) } } } class Ball { // 球 public var x:Number; public var y:Number; public var z:Number; public var color:Vector.; public var vx:Number; public var vy:Number; public var vz:Number; public var radius:Number; public var mass:Number; public function Ball(x:Number, y:Number, z:Number, vx:Number = 0, vy:Number = 0, vz:Number = 0) { this.x = x; this.y = y; this.z = z; this.vx = vx; this.vy = vy; this.vz = vz; radius = 25 + Math.random() * 25; mass = 1 / (radius * radius * radius * 4 / 3 * Math.PI * 0.00025); // 質量の逆数 color = new Vector.(4, true); color[0] = 0.3 + Math.random() * 0.7; color[1] = 0.3 + Math.random() * 0.7; color[2] = 0.3 + Math.random() * 0.7; color[3] = 1; } public function move():void { x += vx; y += vy; if (x > -200 && x < 200 && z > -200 && z < 200 && y < -100 + radius) y = -100 + radius; z += vz; vy -= 0.15; } public function collide(b:Ball):Contact { var dx:Number = x - b.x; var dy:Number = y - b.y; var dz:Number = z - b.z; var d2:Number = dx * dx + dy * dy + dz * dz; if (d2 < (radius + b.radius) * (radius + b.radius)) { var d:Number = 1 / Math.sqrt(d2); dx *= d; dy *= d; dz *= d; return new Contact(null, this, b, dx, dy, dz, 0); } return null; } } var vertexShaderCode:String = ; var fragmentShaderCode:String = ; var fragmentShaderCodeBall:String = ; import flash.display.*; import flash.events.*; import flash.display3D.*; import flash.display3D.textures.*; import flash.geom.*; import flash.net.*; import flash.system.*; import com.adobe.utils.*; import flash.utils.*; class EGraphics { public static const OFFSET_VERTEX:uint = 0; public static const OFFSET_NORMAL:uint = 3; public static const OFFSET_COLOR:uint = 6; public static const OFFSET_UV:uint = 10; public static const NUM_DATAS_IN_VERTEX:uint = 13; // private var s3d:Stage3D; private var c3d:Context3D; private var p3d:Program3D; private var callback:Function; private var width:int; private var height:int; // private var modelview:Matrix3D; private var projection:Matrix3D; public function EGraphics(s3d:Stage3D, width:int, height:int, callback:Function) { this.s3d = s3d; this.width = width; this.height = height; this.callback = callback; s3d.addEventListener(Event.CONTEXT3D_CREATE, init); s3d.requestContext3D(Context3DRenderMode.AUTO); } private function init(e:Event):void { c3d = s3d.context3D; c3d.enableErrorChecking = true; c3d.configureBackBuffer(width, height, 0, true); c3d.setBlendFactors(Context3DBlendFactor.SOURCE_ALPHA, Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA); p3d = c3d.createProgram(); c3d.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 8, Vector.([0, 0.5, 1, 2])); c3d.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 9, Vector.([-1, 8, 16, 32])); c3d.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 10, Vector.([0.03125, 0.0625, 0.125, 0.25])); c3d.setProgram(p3d); callback(); } public function setShader(shader:EShader):void { c3d.setProgram(shader.program3D); } public function renderMesh(mesh:EMesh):void { var Vertexbuf:VertexBuffer3D = mesh.vertexBuffer; c3d.setVertexBufferAt(0, Vertexbuf, OFFSET_VERTEX, Context3DVertexBufferFormat.FLOAT_3); c3d.setVertexBufferAt(1, Vertexbuf, OFFSET_COLOR, Context3DVertexBufferFormat.FLOAT_4); c3d.setVertexBufferAt(2, Vertexbuf, OFFSET_NORMAL, Context3DVertexBufferFormat.FLOAT_3); c3d.setVertexBufferAt(3, Vertexbuf, OFFSET_UV, Context3DVertexBufferFormat.FLOAT_2); Vertexbuf.uploadFromVector(mesh.vertices, 0, mesh.numVertices); var idxbuf:IndexBuffer3D = mesh.indexBuffer; idxbuf.uploadFromVector(mesh.indices, 0, mesh.numIndices); c3d.drawTriangles(idxbuf); } public function createVertexBuffer(numVertices:uint):VertexBuffer3D { return c3d.createVertexBuffer(numVertices, NUM_DATAS_IN_VERTEX); } public function createIndexBuffer(numIndices:uint):IndexBuffer3D { return c3d.createIndexBuffer(numIndices); } public function beginScene(r:Number = 0, g:Number = 0, b:Number = 0):void { c3d.clear(r, g, b, 1); } public function endScene():void { c3d.present(); } public function get stage3D():Stage3D { return s3d; } public function get context3D():Context3D { return c3d; } } class EMesh { private var vts:Vector.; private var ids:Vector.; // private var Vertexbuf:VertexBuffer3D; private var idxbuf:IndexBuffer3D; // private var numVertex:uint; private var numfce:uint; public function EMesh() { vts = new Vector.(); ids = new Vector.(); // Vertexbuf = null; idxbuf = null; // numVertex = 0; numfce = 0; } public function addVertex(x:Number, y:Number, z:Number, u:Number = 0, v:Number = 0, r:Number = 1, g:Number = 1, b:Number = 1):void { var idx:uint = numVertex * EGraphics.NUM_DATAS_IN_VERTEX; vts.length += EGraphics.NUM_DATAS_IN_VERTEX; var i:uint = idx + EGraphics.OFFSET_VERTEX; vts[i] = x; vts[i + 1] = y; vts[i + 2] = z; i = idx + EGraphics.OFFSET_COLOR; vts[i] = r; vts[i + 1] = g; vts[i + 2] = b; i = idx + EGraphics.OFFSET_UV; vts[i] = u; vts[i + 1] = v; numVertex++; } public function addFace(i1:uint, i2:uint, i3:uint):void { var idx:uint = numfce * 3; ids[idx++] = i1; ids[idx++] = i2; ids[idx++] = i3; numfce++; } public function calcNormals():void { // 頂点法線ベクトルを計算 const numDatasInVertex:uint = EGraphics.NUM_DATAS_IN_VERTEX; const offsetVertex:uint = EGraphics.OFFSET_VERTEX; const offsetNormal:uint = EGraphics.OFFSET_NORMAL; var idx:uint = 0; var i:uint; var num:uint = numVertex * numDatasInVertex; for (i = offsetNormal; i < num; i += numDatasInVertex) { vts[i] = 0; vts[i + 1] = 0; vts[i + 2] = 0; } for (i = 0; i < numfce; i++) { var i1:uint = ids[idx] * numDatasInVertex; var i2:uint = ids[idx + 1] * numDatasInVertex; var i3:uint = ids[idx + 2] * numDatasInVertex; var v1:uint = i1 + offsetVertex; var v2:uint = i2 + offsetVertex; var v3:uint = i3 + offsetVertex; // var x1:Number = vts[v2] - vts[v1]; var y1:Number = vts[v2 + 1] - vts[v1 + 1]; var z1:Number = vts[v2 + 2] - vts[v1 + 2]; var x2:Number = vts[v3] - vts[v1]; var y2:Number = vts[v3 + 1] - vts[v1 + 1]; var z2:Number = vts[v3 + 2] - vts[v1 + 2]; // var nx:Number = y2 * z1 - z2 * y1; var ny:Number = z2 * x1 - x2 * z1; var nz:Number = x2 * y1 - y2 * x1; var nl:Number = 1 / Math.sqrt(nx * nx + ny * ny + nz * nz); nx *= nl; ny *= nl; nz *= nl; // v1 = i1 + offsetNormal; v2 = i2 + offsetNormal; v3 = i3 + offsetNormal; vts[v1] += nx; vts[v1 + 1] += ny; vts[v1 + 2] += nz; vts[v2] += nx; vts[v2 + 1] += ny; vts[v2 + 2] += nz; vts[v3] += nx; vts[v3 + 1] += ny; vts[v3 + 2] += nz; // 正規化はシェーダーで行う idx += 3; } } public function initVertexBuffer(g:EGraphics):void { Vertexbuf = g.createVertexBuffer(numVertex); } public function initIndexBuffer(g:EGraphics):void { idxbuf = g.createIndexBuffer(numfce * 3); } public function get numVertices():uint { return numVertex; } public function get numIndices():uint { return numfce * 3; } public function get vertices():Vector. { return vts; } public function get indices():Vector. { return ids; } public function get vertexBuffer():VertexBuffer3D { return Vertexbuf; } public function get indexBuffer():IndexBuffer3D { return idxbuf; } } class EShader { private var Vertexcode:ByteArray; private var frgcode:ByteArray; private var p3d:Program3D; public function EShader(vertex:String, fragment:String, c3d:Context3D) { var agal:AGALMiniAssembler = new AGALMiniAssembler(); agal.assemble(Context3DProgramType.VERTEX, vertex); Vertexcode = agal.agalcode; agal.assemble(Context3DProgramType.FRAGMENT, fragment); frgcode = agal.agalcode; p3d = c3d.createProgram(); p3d.upload(Vertexcode, frgcode); } public function get vertexShader():ByteArray { return Vertexcode; } public function get fragmentShader():ByteArray { return frgcode; } public function get program3D():Program3D { return p3d; } }