forked from LeenkxTeam/LNXSDK
		
	
		
			
	
	
		
			391 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
		
		
			
		
	
	
			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; | ||
|  | } |