Skip to content

Instantly share code, notes, and snippets.

@RudenkoArts RudenkoArts/kha_bunnies_test.hx Secret
Created Apr 29, 2020

Embed
What would you like to do?
package;
import kha.Assets;
import kha.Framebuffer;
import kha.Scheduler;
import kha.System;
import kha.Color;
import kha.Image;
import kha.Shaders;
import kha.graphics4.ConstantLocation;
import kha.graphics4.FragmentShader;
import kha.graphics4.IndexBuffer;
import kha.graphics4.PipelineState;
import kha.graphics4.TextureUnit;
import kha.graphics4.TextureAddressing;
import kha.graphics4.TextureFilter;
import kha.graphics4.MipMapFilter;
import kha.graphics4.Usage;
import kha.graphics4.VertexBuffer;
import kha.graphics4.VertexData;
import kha.graphics4.VertexStructure;
import kha.graphics4.BlendingFactor;
import kha.graphics4.BlendingOperation;
import kha.math.FastMatrix4;
import kha.arrays.Float32Array;
import kha.input.KeyCode;
class Main {
static var bunniesAll:Array<Bunnie>;
static var bunniesDraw:Array<Array<Bunnie>>;
static var bunnieBatchTypes:Array<Int>;
static var bunnieImages:Array<Image>;
// static var incBunnies:Int = 1000;
static var numBunnies:Int = 0;
static var gravity:Float = 90;
static var bunnieSize:Float = 32;
static var width:Int;
static var height:Int;
static var dtAverage:Float = 0;
static var dtAverageAccum:Float = 0;
static var dtAverage_span:Int = 60;
static var dtAverageCount:Int = 0;
static var lastTime:Float = 0;
static var bufferSize:Int = 1000;
static var pipeline:PipelineState;
static var projectionLocation:ConstantLocation;
static var textureLocation:TextureUnit;
static var verticesTmp:Float32Array;
static var vertices:Float32Array;
static var vertexBuffers:Array<VertexBuffer>;
static var vertexBuffer:VertexBuffer;
static var indexBuffer:IndexBuffer;
static var structure:VertexStructure;
static inline var vertexSize:Int = 9;
static var bufferIndex:Int = 0;
static var texture:Image;
static var projectionMatrix:FastMatrix4;
static var drawType:DrawType;
static var drawCalls:Int = 0;
static var touchCount:Int = 0;
static function init() {
bunniesAll = [];
bunniesDraw = [];
vertexBuffers = [];
bunnieImages = [
Assets.images.bunnie_blue,
Assets.images.bunnie_white,
Assets.images.bunnie_red
];
// type, count
bunnieBatchTypes = [
BunnieType.BLUE, 1000,
BunnieType.WHITE, 10,
BunnieType.RED, 200,
BunnieType.BLUE, 1,
BunnieType.WHITE, 100,
BunnieType.RED, 30,
BunnieType.BLUE, 300,
BunnieType.WHITE, 500,
BunnieType.RED, 800,
];
width = kha.Window.get(0).width;
height = kha.Window.get(0).height;
lastTime = kha.System.time;
drawType = DrawType.G2;
initG4();
}
static function initG4() {
projectionMatrix = FastMatrix4.identity();
projectionMatrix.setFrom(FastMatrix4.orthogonalProjection(0, width, height, 0, 0.1, 1000));
structure = new VertexStructure();
structure.add("vertexPosition", VertexData.Float3);
structure.add("texPosition", VertexData.Float2);
structure.add("vertexColor", VertexData.Float4);
pipeline = new PipelineState();
pipeline.fragmentShader = Shaders.painter_image_frag;
pipeline.vertexShader = Shaders.painter_image_vert;
pipeline.inputLayout = [structure];
pipeline.blendSource = BlendingFactor.BlendOne;
pipeline.blendDestination = BlendingFactor.InverseSourceAlpha;
pipeline.alphaBlendSource = BlendingFactor.BlendOne;
pipeline.alphaBlendDestination = BlendingFactor.InverseSourceAlpha;
pipeline.compile();
try { projectionLocation = pipeline.getConstantLocation("projectionMatrix"); } catch (x: Dynamic) { trace(x); }
try { textureLocation = pipeline.getTextureUnit("tex"); } catch (x: Dynamic) { trace(x); }
verticesTmp = new Float32Array(bufferSize * vertexSize * 4);
vertexBuffer = new VertexBuffer(bufferSize * 4, structure, Usage.DynamicUsage);
vertices = vertexBuffer.lock();
indexBuffer = new IndexBuffer(bufferSize * 3 * 2, Usage.StaticUsage);
var indices = indexBuffer.lock();
for (i in 0...bufferSize) {
indices[i * 3 * 2 + 0] = i * 4 + 0;
indices[i * 3 * 2 + 1] = i * 4 + 1;
indices[i * 3 * 2 + 2] = i * 4 + 2;
indices[i * 3 * 2 + 3] = i * 4 + 0;
indices[i * 3 * 2 + 4] = i * 4 + 2;
indices[i * 3 * 2 + 5] = i * 4 + 3;
}
indexBuffer.unlock();
}
static function onTouchStart(id:Int, x:Int, y:Int) {
touchCount++;
if(touchCount == 1) {
drawType = (drawType+1) % 3;
} else if(touchCount == 2) {
addBunnies(0, 0);
}
}
static function onTouchEnd(id:Int, x:Int, y:Int) {
touchCount--;
}
static function onPressed(button:Int, x:Int, y:Int) {
addBunnies(0, 0);
}
static function onKeyDown(key:KeyCode) {
if(key == KeyCode.One) {
drawType = DrawType.G2;
} else if(key == KeyCode.Two) {
drawType = DrawType.G4_ONE_BUFFER;
} else if(key == KeyCode.Three) {
drawType = DrawType.G4_MULT_BUFFERS;
}
}
static function update() {
var dt = 1/60;
for (b in bunniesAll) {
b.x += b.vx * dt;
b.vy += gravity * dt;
b.y += b.vy * dt;
if (b.x > width-bunnieSize) {
b.vx *= -1;
b.x = width-bunnieSize;
} else if (b.x < 0) {
b.vx *= -1;
b.x = 0;
}
if (b.y > height-bunnieSize) {
b.vy *= -0.8;
b.y = height-bunnieSize;
if (Math.random() > 0.5) {
b.vy -= 50 + Math.random() * 60;
}
} else if (b.y < 0) {
b.vy *= -0.8;
b.y = 0;
}
}
}
static function render(framebuffer:Framebuffer) {
var time = kha.System.time;
dtAverageAccum += time - lastTime;
dtAverageCount++;
if(dtAverageCount == dtAverage_span - 1) {
dtAverage = dtAverageAccum/dtAverage_span;
dtAverageAccum = dtAverage;
dtAverageCount = 0;
}
lastTime = time;
drawCalls = 0;
var g = framebuffer.g2;
g.begin();
if(drawType == DrawType.G4_ONE_BUFFER) {
vertices = vertexBuffer.lock();
drawBunniesG4(framebuffer, true);
} else if(drawType == DrawType.G4_MULT_BUFFERS) {
vertices = verticesTmp;
drawBunniesG4(framebuffer, false);
} else if(drawType == DrawType.G2) {
drawBunniesG2(g);
}
g.font = kha.Assets.fonts.Muli_Bold;
g.fontSize = 24;
g.drawString('FPS: ${Math.round(1/dtAverage)}', width - 100, 16);
g.drawString('Bunnies: ${numBunnies}', 16, 16);
g.fontSize = 16;
g.drawString('DrawType: ${getDrawTypeName(drawType)}, DrawCalls: $drawCalls', width/2-100, 16);
g.end();
}
static function drawBunniesG2(g:kha.graphics2.Graphics) {
for (bunnies in bunniesDraw) {
for (b in bunnies) {
g.drawScaledImage(b.image, b.x, b.y, b.w, b.h);
}
}
}
static function drawBunniesG4(framebuffer:Framebuffer, oneBuffer:Bool) {
texture = null;
var g = framebuffer.g4;
var currentImage:Image;
for (bunnies in bunniesDraw) {
for (b in bunnies) {
if(bufferIndex+1 >= bufferSize) {
drawBuffer(g, oneBuffer);
}
if(texture != b.image) {
drawBuffer(g, oneBuffer);
texture = b.image;
}
setVertices(b.x, b.y, b.w, b.h);
}
}
drawBuffer(g, oneBuffer);
}
static function setVertices(x:Float, y:Float, w:Float, h:Float) {
var baseIndex: Int = bufferIndex * vertexSize * 4;
// vertex0
// pos
vertices.set(baseIndex++, x);
vertices.set(baseIndex++, y);
vertices.set(baseIndex++, -5.0);
// tcoord
vertices.set(baseIndex++, 0);
vertices.set(baseIndex++, 0);
// color
vertices.set(baseIndex++, 1);
vertices.set(baseIndex++, 1);
vertices.set(baseIndex++, 1);
vertices.set(baseIndex++, 1);
// vertex1
// pos
vertices.set(baseIndex++, x + w);
vertices.set(baseIndex++, y);
vertices.set(baseIndex++, -5.0);
// tcoord
vertices.set(baseIndex++, 1);
vertices.set(baseIndex++, 0);
// color
vertices.set(baseIndex++, 1);
vertices.set(baseIndex++, 1);
vertices.set(baseIndex++, 1);
vertices.set(baseIndex++, 1);
// vertex2
// pos
vertices.set(baseIndex++, x + w);
vertices.set(baseIndex++, y + h);
vertices.set(baseIndex++, -5.0);
// tcoord
vertices.set(baseIndex++, 1);
vertices.set(baseIndex++, 1);
// color
vertices.set(baseIndex++, 1);
vertices.set(baseIndex++, 1);
vertices.set(baseIndex++, 1);
vertices.set(baseIndex++, 1);
// vertex3
// pos
vertices.set(baseIndex++, x);
vertices.set(baseIndex++, y + h);
vertices.set(baseIndex++, -5.0);
// tcoord
vertices.set(baseIndex++, 0);
vertices.set(baseIndex++, 1);
// color
vertices.set(baseIndex++, 1);
vertices.set(baseIndex++, 1);
vertices.set(baseIndex++, 1);
vertices.set(baseIndex++, 1);
bufferIndex++;
}
static function drawBuffer(g:kha.graphics4.Graphics, oneBuffer:Bool) {
if(bufferIndex > 0) {
var vbo = vertexBuffer;
if(!oneBuffer) {
vbo = getVertexBuffer(bufferIndex * 4);
var verts = vbo.lock();
var len = bufferIndex * vertexSize * 4;
for (i in 0...len) {
verts[i] = vertices[i];
}
}
vbo.unlock(bufferIndex * 4);
g.setPipeline(pipeline);
g.setVertexBuffer(vbo);
g.setIndexBuffer(indexBuffer);
g.setTexture(textureLocation, texture);
g.setTextureParameters(textureLocation, TextureAddressing.Repeat, TextureAddressing.Repeat, TextureFilter.LinearFilter, TextureFilter.LinearFilter, MipMapFilter.NoMipFilter);
g.setMatrix(projectionLocation, projectionMatrix);
g.drawIndexedVertices(0, bufferIndex * 2 * 3);
g.setTexture(textureLocation, null);
bufferIndex = 0;
drawCalls++;
}
}
static function addBunnies(x:Float, y:Float) {
var i = 0;
while(i < bunnieBatchTypes.length) {
var arr = [];
var type = bunnieBatchTypes[i];
var count = bunnieBatchTypes[i+1];
for (j in 0...count) {
arr.push(createBunnie(type, x, y));
}
bunniesDraw.push(arr);
i += 2;
}
}
static function createBunnie(type:BunnieType, x:Float, y:Float):Bunnie {
var b = new Bunnie(bunnieImages[type], x, y, bunnieSize, bunnieSize);
bunniesAll.push(b);
numBunnies++;
return b;
}
static function getVertexBuffer(vertsCount:Int):VertexBuffer {
var p2 = pow2get(vertsCount);
var idx = log2(p2);
var buffer = vertexBuffers[idx];
if(buffer == null) {
buffer = new VertexBuffer(p2, structure, Usage.DynamicUsage);
vertexBuffers[idx] = buffer;
}
return buffer;
}
static function getDrawTypeName(t:DrawType):String {
return switch (t) {
case DrawType.G2: 'G2';
case DrawType.G4_ONE_BUFFER: 'G4_ONE_BUFFER';
case DrawType.G4_MULT_BUFFERS: 'G4_MULT_BUFFERS';
}
}
static function log2(v:Int):Int {
var r;
var shift;
r = v > 0xFFFF? 1 << 4 : 0; v >>>= r;
shift = v > 0xFF ? 1 << 3 : 0; v >>>= shift; r |= shift;
shift = v > 0xF ? 1 << 2 : 0; v >>>= shift; r |= shift;
shift = v > 0x3 ? 1 << 1 : 0; v >>>= shift; r |= shift;
return r | (v >> 1);
}
static function pow2get(x:Int) {
if(x == 0) {
return 1;
}
--x;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
return x + 1;
}
public static function main() {
System.start({title: "Kha", width: 960, height: 640}, function (_) {
Assets.loadEverything(function () {
init();
Scheduler.addTimeTask(function () { update(); }, 0, 1 / 60);
System.notifyOnFrames(function (framebuffers) { render(framebuffers[0]); });
#if kha_android
kha.input.Surface.get().notify(onTouchStart, onTouchEnd, null);
#else
kha.input.Mouse.get().notify(onPressed, null, null, null);
kha.input.Keyboard.get().notify(onKeyDown, null, null);
#end
});
});
}
}
enum abstract BunnieType(Int) from Int to Int {
var BLUE = 0;
var WHITE = 1;
var RED = 2;
}
enum abstract DrawType(Int) from Int to Int {
var G2 = 0;
var G4_ONE_BUFFER = 1;
var G4_MULT_BUFFERS = 2;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.