2025-01-22 16:18:30 +01:00

391 lines
12 KiB
Haxe

package iron.data;
import haxe.ds.Vector;
import kha.graphics4.VertexBuffer;
import kha.graphics4.IndexBuffer;
import kha.graphics4.Usage;
import kha.graphics4.VertexStructure;
import kha.graphics4.VertexData;
import kha.arrays.ByteArray;
import kha.arrays.Float32Array;
import kha.arrays.Uint32Array;
import kha.arrays.Int16Array;
import iron.math.Vec4;
import iron.math.Mat4;
import iron.data.SceneFormat;
import iron.data.MeshData;
class Geometry {
#if lnx_deinterleaved
public var vertexBuffers: Vector<InterleavedVertexBuffer>;
#else
public var vertexBuffer: VertexBuffer;
public var vertexBufferMap: Map<String, VertexBuffer> = new Map();
#end
public var indexBuffers: Array<IndexBuffer>;
public var start = 0; // For drawIndexedVertices
public var count = -1;
public var name = "";
public var ready = false;
public var vertices: ByteArray;
public var indices: Array<Uint32Array>;
public var numTris = 0;
public var materialIndices: Array<Int>;
public var struct: VertexStructure;
public var structLength: Int;
public var structStr: String;
public var usage: Usage;
public var instancedVB: VertexBuffer = null;
public var instanced = false;
public var instanceCount = 0;
public var positions: TVertexArray;
public var normals: TVertexArray;
public var uvs: TVertexArray;
public var cols: TVertexArray;
public var vertexArrays: Array<TVertexArray>;
var data: MeshData;
public var aabb: Vec4 = null;
public var aabbMin: Vec4 = null;
public var aabbMax: Vec4 = null;
// Skinned
#if lnx_skin
public var skeletonTransformsI: Array<Mat4> = null;
#end
public function new(data: MeshData, indices: Array<Uint32Array>, materialIndices: Array<Int>, usage: Usage = null) {
if (usage == null) usage = Usage.StaticUsage;
this.indices = indices;
this.materialIndices = materialIndices;
this.usage = usage;
this.vertexArrays = data.raw.vertex_arrays;
this.positions = getVArray('pos');
this.normals = getVArray('nor');
this.uvs = getVArray('tex');
this.cols = getVArray('col');
this.data = data;
struct = getVertexStructure(vertexArrays);
structLength = Std.int(struct.byteSize() / 2);
structStr = "";
for (e in struct.elements) structStr += e.name;
}
public function delete() {
#if lnx_deinterleaved
for (buf in vertexBuffers) if (buf.buffer != null) buf.buffer.delete();
#else
for (buf in vertexBufferMap) if (buf != null) buf.delete();
#end
for (buf in indexBuffers) buf.delete();
}
static function getVertexStructure(vertexArrays: Array<TVertexArray>): VertexStructure {
var structure = new VertexStructure();
for (i in 0...vertexArrays.length) {
structure.add(vertexArrays[i].attrib, getVertexData(vertexArrays[i].data));
}
return structure;
}
static function getVertexData(data: String): VertexData {
switch (data) {
case "short4norm": return VertexData.Short4Norm;
case "short2norm": return VertexData.Short2Norm;
default: return VertexData.Short4Norm;
}
}
public function applyScale(sx: Float, sy: Float, sz: Float) {
data.scalePos *= sx;
}
public function getVArray(name: String): TVertexArray {
for (i in 0...vertexArrays.length) {
if (vertexArrays[i].attrib == name) {
return vertexArrays[i];
}
}
return null;
}
public function setupInstanced(data: Float32Array, instancedType: Int, usage: Usage) {
var structure = new VertexStructure();
structure.instanced = true;
instanced = true;
// pos, pos+rot, pos+scale, pos+rot+scale
structure.add("ipos", kha.graphics4.VertexData.Float3);
if (instancedType == 2 || instancedType == 4) {
structure.add("irot", kha.graphics4.VertexData.Float3);
}
if (instancedType == 3 || instancedType == 4) {
structure.add("iscl", kha.graphics4.VertexData.Float3);
}
instanceCount = Std.int(data.length / Std.int(structure.byteSize() / 4));
instancedVB = new VertexBuffer(instanceCount, structure, usage, 1);
var vertices = instancedVB.lock();
for (i in 0...Std.int(vertices.byteLength / 4)) vertices.setFloat32(i * 4, data[i]);
instancedVB.unlock();
}
public function copyVertices(vertices: ByteArray, offset = 0, fakeUVs = false) {
buildVertices(vertices, vertexArrays, offset, fakeUVs);
}
static function buildVertices(vertices: ByteArray, vertexArrays: Array<TVertexArray>, offset = 0, fakeUVs = false, uvsIndex = -1) {
var numVertices = verticesCount(vertexArrays[0]);
var di = -1 + offset;
for (i in 0...numVertices) {
for (va in 0...vertexArrays.length) {
var l = vertexArrays[va].size;
if (fakeUVs && va == uvsIndex) { // Add fake uvs if uvs where "asked" for but not found
for (j in 0...l) vertices.setInt16(++di * 2, 0);
continue;
}
for (o in 0...l) {
vertices.setInt16(++di * 2, vertexArrays[va].values[i * l + o]);
}
if (vertexArrays[va].padding != null) {
if (vertexArrays[va].padding == 1) {
vertices.setInt16(++di * 2, 0);
}
}
}
}
}
public function getVerticesLength(): Int {
var res = 0;
for (i in 0...vertexArrays.length) {
res += vertexArrays[i].values.length;
}
return res;
}
#if lnx_deinterleaved
public function get(vs: Array<TVertexElement>): Array<VertexBuffer> {
var vbs = [];
for (e in vs) {
for (v in 0...vertexBuffers.length)
if (vertexBuffers[v].name == e.name) {
vbs.push(vertexBuffers[v].buffer);
continue;
}
if (e.name == "ipos" && instancedVB != null) {
vbs.push(instancedVB);
}
}
return vbs;
}
#else
public function get(vs: Array<TVertexElement>): VertexBuffer {
var key = "";
for (e in vs) key += e.name;
var vb = vertexBufferMap.get(key);
if (vb == null) {
var nVertexArrays = [];
var atex = false;
var texOffset = -1;
var acol = false;
for (e in 0...vs.length) {
if (vs[e].name == "tex") {
atex = true;
texOffset = e;
}
if (vs[e].name == "col") {
acol = true;
}
for (va in 0...vertexArrays.length) {
if (vs[e].name == vertexArrays[va].attrib) {
nVertexArrays.push(vertexArrays[va]);
}
}
}
// Multi-mat mesh with different vertex structures
var struct = getVertexStructure(nVertexArrays);
vb = new VertexBuffer(Std.int(positions.values.length / positions.size), struct, usage);
vertices = vb.lock();
buildVertices(vertices, nVertexArrays, 0, atex && uvs == null, texOffset);
vb.unlock();
vertexBufferMap.set(key, vb);
if (atex && uvs == null) trace("Leenkx Warning: Geometry " + name + " is missing UV map");
if (acol && cols == null) trace("Leenkx Warning: Geometry " + name + " is missing vertex colors");
}
return vb;
}
#end
public function build() {
if (ready) return;
#if lnx_deinterleaved
var vaLength = vertexArrays.length;
vertexBuffers = new Vector(vaLength);
for (i in 0...vaLength)
vertexBuffers[i] = {
name: vertexArrays[i].attrib,
buffer: makeDeinterleavedVB(vertexArrays[i].values, vertexArrays[i].attrib, vertexArrays[i].size)
};
#else
vertexBuffer = new VertexBuffer(Std.int(positions.values.length / positions.size), struct, usage);
vertices = vertexBuffer.lock();
buildVertices(vertices, vertexArrays);
vertexBuffer.unlock();
vertexBufferMap.set(structStr, vertexBuffer);
#end
indexBuffers = [];
for (id in indices) {
if (id.length == 0) continue;
var indexBuffer = new IndexBuffer(id.length, usage);
numTris += Std.int(id.length / 3);
var indicesA = indexBuffer.lock();
for (i in 0...indicesA.length) indicesA[i] = id[i];
indexBuffer.unlock();
indexBuffers.push(indexBuffer);
}
// Instanced
if (data.raw.instanced_data != null) setupInstanced(data.raw.instanced_data, data.raw.instanced_type, usage);
ready = true;
}
#if lnx_deinterleaved
function makeDeinterleavedVB(data: ByteArray, name: String, structLength: Int): VertexBuffer {
var struct = new VertexStructure();
struct.add(name, structLength == 2 ? VertexData.Short2Norm : VertexData.Short4Norm);
var vertexBuffer = new VertexBuffer(Std.int(data.byteLength / 2 / structLength), struct, usage);
var vertices = vertexBuffer.lock();
for (i in 0...Std.int(vertices.byteLength / 2)) vertices.setInt16(i * 2, data.getInt16(i * 2));
vertexBuffer.unlock();
return vertexBuffer;
}
#end
public function getVerticesCount(): Int {
return Std.int(positions.values.length / positions.size);
}
inline static function verticesCount(arr: TVertexArray): Int {
return Std.int(arr.values.length / arr.size);
}
// Skinned
#if lnx_skin
public function initSkeletonTransforms(transformsI: Array<Float32Array>) {
skeletonTransformsI = [];
for (t in transformsI) {
var mi = Mat4.fromFloat32Array(t);
skeletonTransformsI.push(mi);
}
}
#end
public function calculateAABB() {
aabbMin = new Vec4(-0.01, -0.01, -0.01);
aabbMax = new Vec4(0.01, 0.01, 0.01);
aabb = new Vec4();
var i = 0;
while (i < positions.values.length) {
if (positions.values[i ] > aabbMax.x) aabbMax.x = positions.values[i];
if (positions.values[i + 1] > aabbMax.y) aabbMax.y = positions.values[i + 1];
if (positions.values[i + 2] > aabbMax.z) aabbMax.z = positions.values[i + 2];
if (positions.values[i ] < aabbMin.x) aabbMin.x = positions.values[i];
if (positions.values[i + 1] < aabbMin.y) aabbMin.y = positions.values[i + 1];
if (positions.values[i + 2] < aabbMin.z) aabbMin.z = positions.values[i + 2];
i += 4;
}
aabb.x = (Math.abs(aabbMin.x) + Math.abs(aabbMax.x)) / 32767 * data.scalePos;
aabb.y = (Math.abs(aabbMin.y) + Math.abs(aabbMax.y)) / 32767 * data.scalePos;
aabb.z = (Math.abs(aabbMin.z) + Math.abs(aabbMax.z)) / 32767 * data.scalePos;
}
public function calculateTangents() {
// var num_verts = Std.int(positions.length / 4);
// var tangents = new Float32Array(num_verts * 3);
// var bitangents = new Float32Array(num_verts * 3);
// for (ia in indices) {
// var num_tris = Std.int(ia.length / 3);
// for (i in 0...num_tris) {
// var i0 = ia[i * 3 ];
// var i1 = ia[i * 3 + 1];
// var i2 = ia[i * 3 + 2];
// var v0 = Vector((positions[i0 * 4], positions[i0 * 4 + 1], positions[i0 * 4 + 2]));
// var v1 = Vector((positions[i1 * 4], positions[i1 * 4 + 1], positions[i1 * 4 + 2]));
// var v2 = Vector((positions[i2 * 4], positions[i2 * 4 + 1], positions[i2 * 4 + 2]));
// var uv0 = Vector((uvs[i0 * 2], uvs[i0 * 2 + 1]));
// var uv1 = Vector((uvs[i1 * 2], uvs[i1 * 2 + 1]));
// var uv2 = Vector((uvs[i2 * 2], uvs[i2 * 2 + 1]));
// var deltaPos1 = v1 - v0;
// var deltaPos2 = v2 - v0;
// var deltaUV1 = uv1 - uv0;
// var deltaUV2 = uv2 - uv0;
// var d = (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x);
// var r = d != 0 ? 1.0 / d : 1.0;
// var tangent = (deltaPos1 * deltaUV2.y - deltaPos2 * deltaUV1.y) * r;
// var bitangent = (deltaPos2 * deltaUV1.x - deltaPos1 * deltaUV2.x) * r;
// var tangents[i0 * 3 ] += tangent.x;
// var tangents[i0 * 3 + 1] += tangent.y;
// var tangents[i0 * 3 + 2] += tangent.z;
// var tangents[i1 * 3 ] += tangent.x;
// var tangents[i1 * 3 + 1] += tangent.y;
// var tangents[i1 * 3 + 2] += tangent.z;
// var tangents[i2 * 3 ] += tangent.x;
// var tangents[i2 * 3 + 1] += tangent.y;
// var tangents[i2 * 3 + 2] += tangent.z;
// var bitangents[i0 * 3 ] += bitangent.x;
// var bitangents[i0 * 3 + 1] += bitangent.y;
// var bitangents[i0 * 3 + 2] += bitangent.z;
// var bitangents[i1 * 3 ] += bitangent.x;
// var bitangents[i1 * 3 + 1] += bitangent.y;
// var bitangents[i1 * 3 + 2] += bitangent.z;
// var bitangents[i2 * 3 ] += bitangent.x;
// var bitangents[i2 * 3 + 1] += bitangent.y;
// var bitangents[i2 * 3 + 2] += bitangent.z;
// }
// }
// // Orthogonalize
// for (i in 0...num_verts) {
// var t = Vector((tangents[i * 3], tangents[i * 3 + 1], tangents[i * 3 + 2]));
// var b = Vector((bitangents[i * 3], bitangents[i * 3 + 1], bitangents[i * 3 + 2]));
// var n = Vector((normals[i * 2], normals[i * 2 + 1], positions[i * 4 + 3] / scale_pos));
// var v = t - n * n.dot(t);
// v.normalize();
// // Calculate handedness
// var cnv = n.cross(v);
// if (cnv.dot(b) < 0.0)
// v = v * -1.0;
// tangents[i * 3 ] = v.x;
// tangents[i * 3 + 1] = v.y;
// tangents[i * 3 + 2] = v.z;
// }
}
}
#if js
typedef InterleavedVertexBuffer = {
#else
@:structInit class InterleavedVertexBuffer {
#end
public var name: String;
public var buffer: VertexBuffer;
}