-
-
Save Justinfront/0b7acb1a4e9213deaad5 to your computer and use it in GitHub Desktop.
Using TwoLines with Kha to draw 2D vector in 3d.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-cmd node Kha/make -t html5 | |
-cmd node Kha/make --server |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package; | |
// Modified from http://luboslenco.com/kha3d/ | |
// see example 6. | |
import kha.Game; | |
import kha.Framebuffer; | |
import kha.Color; | |
import kha.Loader; | |
import kha.graphics4.Program; | |
import kha.graphics4.VertexStructure; | |
import kha.graphics4.VertexBuffer; | |
import kha.graphics4.IndexBuffer; | |
import kha.graphics4.FragmentShader; | |
import kha.graphics4.VertexShader; | |
import kha.graphics4.VertexData; | |
import kha.graphics4.Usage; | |
import kha.graphics4.ConstantLocation; | |
import kha.graphics4.CompareMode; | |
import kha.math.Matrix4; | |
import kha.math.Vector3; | |
import TwoLines; | |
class Empty extends Game { | |
// An array of vertices to form a cube | |
static var vertices:Array<Float> = []; | |
// Array of colors for each cube vertex | |
static var colors:Array<Float> = []; | |
var vertexBuffer:VertexBuffer; | |
var indexBuffer:IndexBuffer; | |
var program:Program; | |
var mvp:Matrix4; | |
var mvpID:ConstantLocation; | |
public function new() { | |
super("Empty"); | |
} | |
override public function init() { | |
// Define vertex structure | |
var structure = new VertexStructure(); | |
structure.add("pos", VertexData.Float3); | |
structure.add("col", VertexData.Float3); | |
// Save length - we store position and color data | |
var structureLength = 6; | |
// Load shaders - these are located in 'Sources/Shaders' directory | |
// and Kha includes them automatically | |
var fragmentShader = new FragmentShader(Loader.the.getShader("simple.frag")); | |
var vertexShader = new VertexShader(Loader.the.getShader("simple.vert")); | |
// Link program with fragment and vertex shaders we loaded | |
program = new Program(); | |
program.setFragmentShader(fragmentShader); | |
program.setVertexShader(vertexShader); | |
program.link(structure); | |
// Get a handle for our "MVP" uniform | |
mvpID = program.getConstantLocation("MVP"); | |
// Projection matrix: 45° Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 units | |
var projection = Matrix4.perspectiveProjection(45.0, 4.0 / 3.0, 0.1, 100.0); | |
// Or, for an ortho camera | |
//var projection = Matrix4.orthogonalProjection(-10.0, 10.0, -10.0, 10.0, 0.0, 100.0); // In world coordinates | |
// Camera matrix | |
var view = Matrix4.lookAt(new Vector3(4, 3, 3), // Camera is at (4, 3, 3), in World Space | |
new Vector3(0, 0, 0), // and looks at the origin | |
new Vector3(0, -1, 0) // Head is up (set to (0, -1, 0) to look upside-down) | |
); | |
// Model matrix: an identity matrix (model will be at the origin) | |
var model = Matrix4.identity(); | |
// Our ModelViewProjection: multiplication of our 3 matrices | |
// Remember, matrix multiplication is the other way around | |
mvp = Matrix4.identity(); | |
mvp = mvp.multmat(projection); | |
mvp = mvp.multmat(view); | |
mvp = mvp.multmat(model); | |
var verticesLocal = vertices; | |
var colorsLocal = colors; | |
//TwoLines.testColors = true; // sets some default colors. | |
TwoLines.setupSingleColors( 0xff0000, 1, 0xff0000, 1 ); | |
TwoLines.thickness = 50; | |
var twoLines = TwoLines; | |
var toRGBs = toRGB; | |
var z: Float = -1; | |
var adjScale: Float = 200; | |
// create the triangle drawing command. | |
TwoLines.drawTri = function( p0: Point, p1: Point, p2: Point | |
, col: Int, alpha: Float | |
, lineCol: Int, lineAlpha: Float ):Void { | |
verticesLocal.push( p0.x/adjScale - 2); | |
verticesLocal.push( p0.y/adjScale ); | |
verticesLocal.push( -z ); | |
verticesLocal.push( p1.x/adjScale - 2); | |
verticesLocal.push( p1.y/adjScale ); | |
verticesLocal.push( -z ); | |
verticesLocal.push( p2.x/adjScale - 2); | |
verticesLocal.push( p2.y/adjScale ); | |
verticesLocal.push( -z ); | |
var rgb = toRGBs( col ); | |
colors.push( rgb.r ); | |
colors.push( rgb.g ); | |
colors.push( rgb.b ); | |
colors.push( rgb.r ); | |
colors.push( rgb.g ); | |
colors.push( rgb.b ); | |
colors.push( rgb.r ); | |
colors.push( rgb.g ); | |
colors.push( rgb.b ); | |
//twoLines.thickness -= 0.05; | |
} | |
/* | |
// for reference. | |
var image = Image.createRenderTarget(256, 256); | |
image.g2.begin(); | |
image.g2.fillRect(...); | |
image.g2.end();*/ | |
// wavy line | |
z = -0.5; | |
// alpha and outline are not used only first color parameter. | |
TwoLines.setupSingleColors( 0xff0000, 1, 0xff0000, 1 ); | |
TwoLines.createTriangles( TwoLines.horizontalWavePoints( 10, 5, 100, 50, 60, 3 ) ); | |
// circles | |
z = 0.5; | |
TwoLines.setupSingleColors( 0xffff00, 1, 0xffff00, 1 ); | |
TwoLines.createPolyTriangles( TwoLines.polyPoints( 150, 100, 100, 60 ) ); | |
TwoLines.setupSingleColors( 0x0000ff, 1, 0x0000ff, 1 ); | |
TwoLines.createPolyTriangles( TwoLines.polyPoints( 450, 100, 100, 60 ) ); | |
TwoLines.setupSingleColors( 0x00ff00, 1, 0x00ff00, 1 ); | |
TwoLines.createPolyTriangles( TwoLines.polyPoints( 750, 100, 100, 60 ) ); | |
// polygons | |
z = 1; | |
TwoLines.thickness = 80; | |
TwoLines.setupSingleColors( 0xffcc00, 1, 0xffcc00, 1 ); | |
TwoLines.createPolyTriangles( TwoLines.polyPoints( 850, 200, 100, 6 ) ); | |
TwoLines.thickness = 30; | |
TwoLines.setupSingleColors( 0xff00ff, 1, 0xff00ff, 1 ); | |
TwoLines.createPolyTriangles( TwoLines.boxPoints( 100, 100, 200, 200 ) ); | |
TwoLines.thickness = 10; | |
TwoLines.setupSingleColors( 0x00ffff, 1, 0x00ffff, 1 ); | |
TwoLines.createPolyTriangles( TwoLines.equalTriPoints( 570, 240, 100, 0 ) ); | |
// quad curve | |
z = -0.7; | |
TwoLines.setupSingleColors( 0xF84525, 1, 0xF84525, 1 ); | |
TwoLines.thickness = 30; | |
var cx = 300; | |
var cy = -300; | |
var heartScale = 7; | |
var pp: Array<Point> = [ | |
{ x: cx - 27*heartScale, y: cy - 20*heartScale }, | |
{ x: cx - 15*heartScale, y: cy - 30*heartScale }, | |
{ x: cx + 1, y: cy - 15*heartScale }, | |
{ x: cx + 15*heartScale, y: cy - 30*heartScale }, | |
{ x: cx + 27*heartScale, y: cy - 20*heartScale }, | |
{ x: cx + 34*heartScale, y: cy - 5*heartScale }, | |
{ x: cx + 20*heartScale, y: cy + 6*heartScale }, | |
{ x: cx + 25*heartScale, y: cy +1 }, | |
{ x: cx + 1, y: cy + 30*heartScale }, | |
{ x: cx - 25*heartScale, y: cy + 1}, | |
{ x: cx - 20*heartScale, y: cy + 6*heartScale }, | |
{ x: cx - 34*heartScale, y: cy - 5*heartScale }, | |
{ x: cx - 28*heartScale, y: cy - 20*heartScale }, | |
{ x: cx - 27*heartScale, y: cy - 20*heartScale }]; | |
pp.reverse(); | |
for( i in 0...(pp.length-2) ){ | |
if( (i-1)%2 == 0 ){ | |
TwoLines.createTriangles( TwoLines.quadCurve( pp[i], pp[i+1], pp[i+2] ) ); | |
} | |
} | |
// Create vertex buffer | |
vertexBuffer = new VertexBuffer( | |
Std.int(vertices.length / 3), // Vertex count - 3 floats per vertex | |
structure, // Vertex structure | |
Usage.StaticUsage // Vertex data will stay the same | |
); | |
// Copy vertices and colors to vertex buffer | |
var vbData = vertexBuffer.lock(); | |
for (i in 0...Std.int(vbData.length / structureLength)) { | |
vbData[i * structureLength] = vertices[i * 3]; | |
vbData[i * structureLength + 1] = vertices[i * 3 + 1]; | |
vbData[i * structureLength + 2] = vertices[i * 3 + 2]; | |
vbData[i * structureLength + 3] = colors[i * 3]; | |
vbData[i * structureLength + 4] = colors[i * 3 + 1]; | |
vbData[i * structureLength + 5] = colors[i * 3 + 2]; | |
} | |
vertexBuffer.unlock(); | |
// A 'trick' to create indices for a non-indexed vertex data | |
var indices:Array<Int> = []; | |
for (i in 0...Std.int(vertices.length / 3)) { | |
indices.push(i); | |
} | |
// Create index buffer | |
indexBuffer = new IndexBuffer( | |
indices.length, // Number of indices for our cube | |
Usage.StaticUsage // Index data will stay the same | |
); | |
// Copy indices to index buffer | |
var iData = indexBuffer.lock(); | |
for (i in 0...iData.length) { | |
iData[i] = indices[i]; | |
} | |
indexBuffer.unlock(); | |
} | |
public static inline function toRGB( int: Int ) : { r: Float, g: Float, b: Float } { | |
return { | |
r: ((int >> 16) & 255) / 255, | |
g: ((int >> 8) & 255) / 255, | |
b: (int & 255) / 255, | |
} | |
} | |
override public function render( frame: Framebuffer ) { | |
// A graphics object which lets us perform 3D operations | |
var g = frame.g4; | |
// Begin rendering | |
g.begin(); | |
// Set depth mode | |
g.setDepthMode(true, CompareMode.Less); | |
// Clear screen | |
g.clear(Color.fromFloats(0.0, 0.0, 0.3), 1.0); | |
// Bind data we want to draw | |
g.setVertexBuffer(vertexBuffer); | |
g.setIndexBuffer(indexBuffer); | |
// Bind shader program we want to draw with | |
g.setProgram(program); | |
// Set our transformation to the currently bound shader, in the "MVP" uniform | |
g.setMatrix(mvpID, mvp); | |
// Draw! | |
g.drawIndexedVertices(); | |
// End rendering | |
g.end(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package; | |
import kha.Starter; | |
class Main { | |
public static function main() { | |
var starter = new Starter(); | |
starter.start(new Empty()); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package; | |
typedef Point = { x: Float, y: Float } | |
class TwoLines { | |
var p0: Point; | |
var p1: Point; | |
var p2: Point; | |
public var p3: Point; | |
public var p4: Point; | |
var angleA: Float; // smallest angle between lines | |
var cosA: Float; | |
var b2: Float; | |
var c2: Float; | |
var a2: Float; | |
var b: Float; // first line length | |
var c: Float; // second line length | |
var a: Float; | |
var angleD: Float; | |
var halfA: Float; | |
var beta: Float; | |
var r: Float; | |
var _theta: Float; | |
var angle1: Float; | |
var angle2: Float; | |
public function new( p0_: Point, p1_: Point, p2_: Point, thick: Float ){ | |
p0 = p0_; | |
p1 = p1_; | |
p2 = p2_; | |
b2 = dist2( p0, p1 ); | |
c2 = dist2( p1, p2 ); | |
a2 = dist2( p0, p2 ); | |
b = Math.sqrt( b2 ); | |
c = Math.sqrt( c2 ); | |
a = Math.sqrt( a2 ); | |
cosA = ( b2 + c2 - a2 )/ ( 2*b*c ); | |
angleA = Math.acos( cosA ); | |
angleD = Math.PI - angleA; | |
halfA = angleA/2; | |
beta = Math.PI/2 - halfA; | |
r = ( thick/2 )/Math.cos( beta ); | |
calculateP3p4(); | |
} | |
public inline function calculateP3p4(){ | |
_theta = theta( p0, p1 ); | |
if( _theta > 0 ){ | |
if( halfA < 0 ){ | |
angle2 = _theta + halfA + Math.PI/2; | |
angle1 = _theta - halfA; | |
}else { | |
angle1 = _theta + halfA - Math.PI; | |
angle2 = _theta + halfA; | |
} | |
} else { | |
if( halfA > 0 ){ | |
angle1 = _theta + halfA - Math.PI; | |
angle2 = _theta + halfA; | |
} else { | |
angle2 = _theta + halfA + Math.PI/2; | |
angle1 = _theta - halfA; | |
} | |
} | |
p3 = { x: p1.x + r * Math.cos( angle1 ), y: p1.y + r * Math.sin( angle1 ) }; | |
p4 = { x: p1.x + r * Math.cos( angle2 ), y: p1.y + r * Math.sin( angle2 ) }; | |
} | |
public function rebuildAsPoly( p2_: Point ){ | |
p0 = p1; | |
p1 = p2; | |
p2 = p2_; | |
calculateP3p4(); | |
} | |
private function theta( p0: Point, p1: Point ): Float { | |
var dx: Float = p0.x - p1.x; | |
var dy: Float = p0.y - p1.y; | |
return Math.atan2( dy, dx ); | |
} | |
private function dist2( p0: Point, p1: Point ): Float { | |
var dx: Float = p0.x - p1.x; | |
var dy: Float = p0.y - p1.y; | |
return dx*dx + dy*dy; | |
} | |
public static var thickness: Float; | |
private static var q0: Point; | |
private static var q1: Point; | |
public static var col: Int; | |
public static var alpha: Float; | |
public static var lineCol: Int; | |
public static var lineAlpha: Float; | |
public static var col2: Int; | |
public static var alpha2: Float; | |
public static var lineCol2: Int; | |
public static var lineAlpha2: Float; | |
public static function setupSingleColors( col_: Int, alpha_: Float, lineCol_: Int, lineAlpha_: Float ){ | |
col = col_; | |
col2 = col_; | |
alpha = alpha_; | |
alpha2 = alpha_; | |
lineCol = lineCol_; | |
lineCol2 = lineCol_; | |
lineAlpha = lineAlpha_; | |
lineAlpha2 = lineAlpha_; | |
} | |
public static var testColors( null, set ):Bool; | |
public static function set_testColors( val: Bool ): Bool{ | |
col = 0xffff00; | |
col2 = 0xff00ff; | |
lineCol = col; | |
lineCol2 = col2; | |
alpha = 0.5; | |
alpha2 = alpha; | |
lineAlpha = 0.7; | |
lineAlpha2 = lineAlpha; | |
return val; | |
} | |
// p1, p2, p3 | |
// , colour, alpha default 1, optional line color depending on support, optional line alpha depending on support | |
public static var drawTri: Point -> Point -> Point -> Int -> Float -> Int -> Float -> Void; | |
public static inline function createPolyTriangles( p: Array<Point> ){ | |
q0 = p[0]; | |
q1 = p[0]; | |
var twoLines: TwoLines = firstQuad( p, 0 ); | |
for( i in 1...( p.length - 2 ) ) drawOtherQuad( p, twoLines, i ); | |
} | |
public static inline function createTriangles( p: Array<Point> ){ | |
q0 = p[0]; | |
q1 = p[0]; | |
for( i in 0...( p.length - 2 ) ) drawQuad( p, i ); | |
} | |
private static inline function firstQuad( p: Array<Point>, i: Int ): TwoLines { | |
var twoLines = new TwoLines( p[ i ], p[ i + 1 ], p[ i + 2 ], thickness ); | |
var q3 = twoLines.p3; | |
var q4 = twoLines.p4; | |
q0 = q3; | |
q1 = q4; | |
return twoLines; | |
} | |
// assumes that firstQuad is drawn. | |
private static inline function drawOtherQuad( p: Array<Point>, twoLines: TwoLines, i: Int ){ | |
twoLines.rebuildAsPoly( p[ i + 2 ]); | |
var q3 = twoLines.p3; | |
var q4 = twoLines.p4; | |
drawTri( q0, q3, q1, col, alpha, lineCol, lineAlpha ); | |
drawTri( q1, q3, q4, col2, alpha2, lineCol2, lineAlpha2 ); | |
q0 = q3; | |
q1 = q4; | |
return twoLines; | |
} | |
private static inline function drawQuad( p: Array<Point>, i: Int ){ | |
var twoLines = new TwoLines( p[ i ], p[ i + 1 ], p[ i + 2 ], thickness ); | |
var q3 = twoLines.p3; | |
var q4 = twoLines.p4; | |
if( i != 0 ){ | |
drawTri( q0, q3, q1, col, alpha, lineCol, lineAlpha ); | |
drawTri( q1, q3, q4, col2, alpha2, lineCol2, lineAlpha2 ); | |
} | |
q0 = q3; | |
q1 = q4; | |
return twoLines; | |
} | |
public static inline function boxPoints( x: Float,y: Float, wid: Float, hi: Float ): Array<Point>{ | |
var p: Array<Point> = [ { x: x, y: y } | |
, { x: x+wid, y: y } | |
, { x: x+wid, y: y+hi } | |
, { x: x, y: y+hi } | |
, { x: x, y: y } | |
, { x: x+wid, y: y } | |
, { x: x+wid, y: y+hi } | |
]; | |
p.reverse(); | |
return p; | |
} | |
public static inline function equalTriPoints( dx: Float, dy: Float, radius: Float, ?rotation: Float = 0 ):Array<Point>{ | |
var p: Array<Point> = new Array<Point>(); | |
var angle: Float = 0; | |
var offset: Float = - 2.5*Math.PI*2/6 - Math.PI + rotation; | |
for( i in 0...6 ){ | |
angle = i*(Math.PI*2)/3 - offset; | |
p.push( { x: dx + radius * Math.cos( angle ), y: dy + radius * Math.sin( angle ) }); | |
} | |
p.reverse(); | |
return p; | |
} | |
public static inline function polyPoints( dx: Float, dy: Float, radius: Float, sides: Int ):Array<Point>{ | |
var p: Array<Point> = new Array<Point>(); | |
var angle: Float = 0; | |
var angleInc: Float = (Math.PI*2)/sides; | |
for( i in 0...( sides + 3 ) ){ | |
angle = i*angleInc; | |
p.push( { x: dx + radius * Math.cos( angle ), y: dy + radius * Math.sin( angle ) }); | |
} | |
p.reverse(); | |
return p; | |
} | |
public static inline function horizontalWavePoints( x_: Float, dx_: Float, y_: Float, amplitude: Float, sides: Int, repeats: Float ):Array<Point>{ | |
var p: Array<Point> = new Array<Point>(); | |
var dx: Float = 0; | |
var angleInc: Float = (Math.PI*2)/sides; | |
var len: Int = Std.int( sides*repeats ); | |
for( i in 0...len ) p.push( { x: x_ + (dx+=dx_), y: y_ + amplitude * Math.sin( i*angleInc ) }); | |
return p; | |
} | |
public static inline function quadCurve( p0, p1, p2 ): Array<Point> { | |
var p: Array<Point> = new Array<Point>(); | |
var approxDistance = distance( p0, p1 ) + distance( p1, p2 ); | |
var factor = 2; | |
var v: { x: Float, y: Float }; | |
if( approxDistance == 0 ) approxDistance = 0.000001; | |
var step = Math.min( 1/( approxDistance*0.707 ), 0.2 ); | |
var arr = [ p0, p1, p2 ]; | |
var t = 0.0; | |
v = quadraticBezier( 0.0, arr ); | |
p.push( { x: v.x, y: v.y } ); | |
t += step; | |
while( t < 1 ){ | |
v = quadraticBezier( t, arr ); | |
p.push( { x: v.x, y: v.y } ); | |
t += step; | |
} | |
v = quadraticBezier( 1.0, arr ); | |
p.push( { x: v.x, y: v.y } ); | |
//p.reverse(); | |
return p; | |
} | |
public static inline function distance( p0: { x: Float, y: Float } | |
, p1: { x: Float, y: Float } | |
): Float { | |
var x = p0.x - p1.x; | |
var y = p0.y - p1.y; | |
return Math.sqrt( x*x + y*y ); | |
} | |
// from divtastic3 and hxDeadalus wings.data.MathPoints | |
public inline static function quadraticBezier( t: Float | |
, arr: Array<{ x: Float, y: Float }> | |
): { x: Float,y: Float } { | |
return { x: _quadraticBezier( t, arr[0].x, arr[1].x, arr[2].x ) | |
, y: _quadraticBezier( t, arr[0].y, arr[1].y, arr[2].y ) }; | |
} | |
private inline static function _quadraticBezier ( t: Float | |
, startPoint: Float | |
, controlPoint: Float | |
, endPoint: Float | |
): Float { | |
var u = 1 - t; | |
return Math.pow( u, 2) * startPoint + 2 * u * t * controlPoint + Math.pow( t, 2 ) * endPoint; | |
} | |
public static inline function generateMidPoints( arr: Array<{ x: Float, y: Float }> | |
): Array<{ x: Float, y: Float }>{ | |
var out: Array<{ x: Float, y: Float }> = []; | |
var a: { x: Float, y: Float }; | |
var b: { x: Float, y: Float }; | |
var len = arr.length - 2; | |
for( i in 0...len ){ | |
a = arr[ i ]; | |
b = arr[ i + 1 ]; | |
out.push( { x: ( b.x + a.x )/2, y: ( b.y + a.y )/2 }); | |
out.push( { x: b.x, y: b.y } ); | |
} | |
a = arr[0]; | |
out.unshift( { x: a.x, y: a.y } ); | |
out.unshift( { x: a.x, y: a.y } ); | |
b = arr[ arr.length - 1 ]; | |
out.push( { x: b.x, y: b.y } ); | |
out.push( { x: b.x, y: b.y } ); | |
out.push( { x: b.x, y: b.y } ); | |
return out; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment