forked from LeenkxTeam/LNXSDK
		
	merge upstream
This commit is contained in:
		| @ -775,6 +775,7 @@ class Scene { | ||||
| 			// Attach particle systems | ||||
| 			#if lnx_particles | ||||
| 			if (o.particle_refs != null) { | ||||
| 				cast(object, MeshObject).render_emitter = o.render_emitter; | ||||
| 				for (ref in o.particle_refs) cast(object, MeshObject).setupParticleSystem(sceneName, ref); | ||||
| 			} | ||||
| 			#end | ||||
|  | ||||
							
								
								
									
										50
									
								
								leenkx/Sources/iron/format/bmp/Data.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								leenkx/Sources/iron/format/bmp/Data.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | ||||
| /* | ||||
|  * format - Haxe File Formats | ||||
|  * | ||||
|  *  BMP File Format | ||||
|  *  Copyright (C) 2007-2009 Trevor McCauley, Baluta Cristian (hx port) & Robert Sköld (format conversion) | ||||
|  * | ||||
|  * Copyright (c) 2009, The Haxe Project Contributors | ||||
|  * All rights reserved. | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions are met: | ||||
|  * | ||||
|  *   - Redistributions of source code must retain the above copyright | ||||
|  *     notice, this list of conditions and the following disclaimer. | ||||
|  *   - Redistributions in binary form must reproduce the above copyright | ||||
|  *     notice, this list of conditions and the following disclaimer in the | ||||
|  *     documentation and/or other materials provided with the distribution. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY | ||||
|  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||
|  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||
|  * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR | ||||
|  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||
|  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||
|  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||
|  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||
|  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||
|  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | ||||
|  * DAMAGE. | ||||
|  */ | ||||
| package iron.format.bmp; | ||||
|  | ||||
| typedef Data = { | ||||
|     var header : iron.format.bmp.Header; | ||||
|     var pixels : haxe.io.Bytes; | ||||
| #if (haxe_ver < 4) | ||||
|     var colorTable : Null<haxe.io.Bytes>; | ||||
| #else | ||||
|     var ?colorTable : haxe.io.Bytes; | ||||
| #end | ||||
| } | ||||
|  | ||||
| typedef Header = { | ||||
|     var width : Int;          // real width (in pixels) | ||||
|     var height : Int;         // real height (in pixels) | ||||
|     var paddedStride : Int;   // number of bytes in a stride (including padding) | ||||
|     var topToBottom : Bool;   // whether the bitmap is stored top to bottom | ||||
|     var bpp : Int;            // bits per pixel | ||||
|     var dataLength : Int;     // equal to `paddedStride` * `height` | ||||
|     var compression : Int;    // which compression is being used, 0 for no compression | ||||
| } | ||||
							
								
								
									
										122
									
								
								leenkx/Sources/iron/format/bmp/Reader.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								leenkx/Sources/iron/format/bmp/Reader.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,122 @@ | ||||
| /* | ||||
|  * format - Haxe File Formats | ||||
|  * | ||||
|  *  BMP File Format | ||||
|  *  Copyright (C) 2007-2009 Robert Sköld | ||||
|  * | ||||
|  * Copyright (c) 2009, The Haxe Project Contributors | ||||
|  * All rights reserved. | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions are met: | ||||
|  * | ||||
|  *   - Redistributions of source code must retain the above copyright | ||||
|  *     notice, this list of conditions and the following disclaimer. | ||||
|  *   - Redistributions in binary form must reproduce the above copyright | ||||
|  *     notice, this list of conditions and the following disclaimer in the | ||||
|  *     documentation and/or other materials provided with the distribution. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY | ||||
|  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||
|  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||
|  * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR | ||||
|  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||
|  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||
|  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||
|  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||
|  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||
|  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | ||||
|  * DAMAGE. | ||||
|  */ | ||||
|  | ||||
| package iron.format.bmp; | ||||
|  | ||||
| import iron.format.bmp.Data; | ||||
|  | ||||
|  | ||||
| class Reader { | ||||
|  | ||||
| 	var input : haxe.io.Input; | ||||
|  | ||||
| 	public function new( i ) { | ||||
| 		input = i; | ||||
| 	} | ||||
|  | ||||
| 	/**  | ||||
| 	 * Only supports uncompressed 24bpp bitmaps (the most common format). | ||||
| 	 *  | ||||
| 	 * The returned bytes in `Data.pixels` will be in BGR order, and with padding (if present). | ||||
| 	 *  | ||||
| 	 * @see https://msdn.microsoft.com/en-us/library/windows/desktop/dd318229(v=vs.85).aspx | ||||
| 	 * @see https://en.wikipedia.org/wiki/BMP_file_format#Bitmap_file_header | ||||
| 	 */ | ||||
| 	public function read() : format.bmp.Data { | ||||
| 		// Read Header | ||||
| 		for (b in ["B".code, "M".code]) { | ||||
| 			if (input.readByte() != b) throw "Invalid header"; | ||||
| 		} | ||||
| 	 | ||||
| 		var fileSize = input.readInt32(); | ||||
| 		input.readInt32();							// Reserved | ||||
| 		var offset = input.readInt32(); | ||||
|  | ||||
| 		// Read InfoHeader | ||||
| 		var infoHeaderSize = input.readInt32();		// InfoHeader size | ||||
| 		if (infoHeaderSize != 40) { | ||||
| 			throw 'Info headers with size $infoHeaderSize not supported.'; | ||||
| 		} | ||||
| 		var width = input.readInt32();				// Image width (actual, not padded) | ||||
| 		var height = input.readInt32();				// Image height | ||||
| 		var numPlanes = input.readInt16();			// Number of planes | ||||
| 		var bits = input.readInt16();				// Bits per pixel | ||||
| 		var compression = input.readInt32();		// Compression type | ||||
| 		var dataLength = input.readInt32();			// Image data size (includes padding!) | ||||
| 		input.readInt32();							// Horizontal resolution | ||||
| 		input.readInt32();							// Vertical resolution | ||||
| 		var colorsUsed = input.readInt32();			// Colors used (0 when uncompressed) | ||||
| 		input.readInt32();							// Important colors (0 when uncompressed) | ||||
|  | ||||
| 		// If there's no compression, the dataLength may be 0 | ||||
| 		if ( compression == 0 && dataLength == 0 ) dataLength = fileSize - offset; | ||||
|  | ||||
| 		var bytesRead = 54; // total read above | ||||
| 		 | ||||
| 		var colorTable : haxe.io.Bytes = null; | ||||
| 		if ( bits <= 8 ) { | ||||
| 			if ( colorsUsed == 0 ) { | ||||
| 				colorsUsed = Tools.getNumColorsForBitDepth(bits); | ||||
| 			} | ||||
| 			var colorTableLength = 4 * colorsUsed; | ||||
| 			colorTable = haxe.io.Bytes.alloc( colorTableLength ); | ||||
| 			input.readFullBytes( colorTable, 0, colorTableLength ); | ||||
| 			bytesRead += colorTableLength; | ||||
| 		} | ||||
|  | ||||
| 		input.read( offset - bytesRead ); | ||||
| 		 | ||||
| 		var p = haxe.io.Bytes.alloc( dataLength );	 | ||||
| 		 | ||||
| 		// Read Raster Data | ||||
| 		var paddedStride = Tools.computePaddedStride(width, bits); | ||||
| 		var topToBottom = false; | ||||
| 		if ( height < 0 ) { // if bitmap is stored top to bottom | ||||
| 			topToBottom = true; | ||||
| 			height = -height; | ||||
| 		} | ||||
|      | ||||
| 		input.readFullBytes(p, 0, dataLength); | ||||
| 			 | ||||
| 		return { | ||||
| 			header: { | ||||
| 				width: width, | ||||
| 				height: height, | ||||
| 				paddedStride: paddedStride, | ||||
| 				topToBottom: topToBottom, | ||||
| 				bpp: bits, | ||||
| 				dataLength: dataLength, | ||||
| 				compression: compression | ||||
| 			}, | ||||
| 			pixels: p, | ||||
| 			colorTable: colorTable | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										256
									
								
								leenkx/Sources/iron/format/bmp/Tools.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										256
									
								
								leenkx/Sources/iron/format/bmp/Tools.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,256 @@ | ||||
| /* | ||||
|  * format - Haxe File Formats | ||||
|  * | ||||
|  *  BMP File Format | ||||
|  *  Copyright (C) 2007-2009 Trevor McCauley, Baluta Cristian (hx port) & Robert Sköld (format conversion) | ||||
|  * | ||||
|  * Copyright (c) 2009, The Haxe Project Contributors | ||||
|  * All rights reserved. | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions are met: | ||||
|  * | ||||
|  *   - Redistributions of source code must retain the above copyright | ||||
|  *     notice, this list of conditions and the following disclaimer. | ||||
|  *   - Redistributions in binary form must reproduce the above copyright | ||||
|  *     notice, this list of conditions and the following disclaimer in the | ||||
|  *     documentation and/or other materials provided with the distribution. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY | ||||
|  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||
|  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||
|  * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR | ||||
|  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||
|  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||
|  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||
|  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||
|  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||
|  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | ||||
|  * DAMAGE. | ||||
|  */ | ||||
| package iron.format.bmp; | ||||
|  | ||||
|  | ||||
| class Tools { | ||||
|  | ||||
| 	//												  a  r  g  b | ||||
| 	static var ARGB_MAP(default, never):Array<Int> = [0, 1, 2, 3]; | ||||
| 	static var BGRA_MAP(default, never):Array<Int> = [3, 2, 1, 0]; | ||||
|  | ||||
| 	static var COLOR_SIZE(default, never):Int = 4; | ||||
| 	 | ||||
| 	/** | ||||
| 		Extract BMP pixel data (24bpp in BGR format) and expands it to BGRA, removing any padding in the process. | ||||
| 	**/ | ||||
| 	inline static public function extractBGRA( bmp : iron.format.bmp.Data ) : haxe.io.Bytes { | ||||
| 		return _extract32(bmp, BGRA_MAP, 0xFF); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 		Extract BMP pixel data (24bpp in BGR format) and converts it to ARGB. | ||||
| 	**/ | ||||
| 	inline static public function extractARGB( bmp : iron.format.bmp.Data ) : haxe.io.Bytes { | ||||
| 		return _extract32(bmp, ARGB_MAP, 0xFF); | ||||
| 	} | ||||
|    | ||||
| 	/** | ||||
| 		Creates BMP data from bytes in BGRA format for each pixel. | ||||
| 	**/ | ||||
| 	inline static public function buildFromBGRA( width : Int, height : Int, srcBytes : haxe.io.Bytes, topToBottom : Bool = false ) : Data { | ||||
| 		return _buildFrom32(width, height, srcBytes, BGRA_MAP, topToBottom); | ||||
| 	} | ||||
|    | ||||
| 	/** | ||||
| 		Creates BMP data from bytes in ARGB format for each pixel. | ||||
| 	**/ | ||||
| 	inline static public function buildFromARGB( width : Int, height : Int, srcBytes : haxe.io.Bytes, topToBottom : Bool = false ) : Data { | ||||
| 		return _buildFrom32(width, height, srcBytes, ARGB_MAP, topToBottom); | ||||
| 	} | ||||
|  | ||||
| 	inline static public function computePaddedStride(width:Int, bpp:Int):Int { | ||||
| 		return ((((width * bpp) + 31) & ~31) >> 3); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Gets number of colors for indexed palettes | ||||
| 	 */ | ||||
| 	inline static public function getNumColorsForBitDepth(bpp:Int):Int { | ||||
| 		return switch (bpp) { | ||||
| 			case 1: 2; | ||||
| 			case 4: 16; | ||||
| 			case 8: 256; | ||||
| 			case 16: 65536; | ||||
| 			default: throw 'Unsupported bpp $bpp'; | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	 | ||||
| 	// `channelMap` contains indices to map into ARGB (f.e. the mapping for ARGB is [0,1,2,3], while for BGRA is [3,2,1,0]) | ||||
| 	static function _extract32( bmp : iron.format.bmp.Data, channelMap : Array<Int>, alpha : Int = 0xFF) : haxe.io.Bytes { | ||||
| 		var srcBytes = bmp.pixels; | ||||
| 		var dstLen = bmp.header.width * bmp.header.height * 4; | ||||
| 		var dstBytes = haxe.io.Bytes.alloc( dstLen ); | ||||
| 		var srcPaddedStride = bmp.header.paddedStride; | ||||
| 		 | ||||
| 		var yDir = -1; | ||||
| 		var dstPos = 0; | ||||
| 		var srcPos = srcPaddedStride * (bmp.header.height - 1); | ||||
|      | ||||
| 		if ( bmp.header.topToBottom ) { | ||||
| 			yDir = 1; | ||||
| 			srcPos = 0; | ||||
| 		} | ||||
|  | ||||
| 		if ( bmp.header.bpp < 8 || bmp.header.bpp == 16 ) { | ||||
| 			throw 'bpp ${bmp.header.bpp} not supported'; | ||||
| 		} | ||||
|  | ||||
| 		var colorTable:haxe.io.Bytes = null; | ||||
| 		if ( bmp.header.bpp <= 8 ) { | ||||
| 			var colorTableLength = getNumColorsForBitDepth(bmp.header.bpp); | ||||
| 			colorTable = haxe.io.Bytes.alloc(colorTableLength * COLOR_SIZE); | ||||
| 			var definedColorTableLength = Std.int( bmp.colorTable.length / COLOR_SIZE ); | ||||
| 			for( i in 0...definedColorTableLength ) { | ||||
| 				var b = bmp.colorTable.get( i * COLOR_SIZE); | ||||
| 				var g = bmp.colorTable.get( i * COLOR_SIZE + 1); | ||||
| 				var r = bmp.colorTable.get( i * COLOR_SIZE + 2); | ||||
|  | ||||
| 				colorTable.set(i * COLOR_SIZE + channelMap[0], alpha); | ||||
| 				colorTable.set(i * COLOR_SIZE + channelMap[1], r); | ||||
| 				colorTable.set(i * COLOR_SIZE + channelMap[2], g); | ||||
| 				colorTable.set(i * COLOR_SIZE + channelMap[3], b); | ||||
| 			} | ||||
| 			// We want to have the table the full length in case indices outside the range are present | ||||
| 			colorTable.fill(definedColorTableLength, colorTableLength - definedColorTableLength, 0); | ||||
| 			for( i in definedColorTableLength...colorTableLength ) { | ||||
| 				colorTable.set(i * COLOR_SIZE + channelMap[0], alpha); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		switch bmp.header.compression { | ||||
| 			case 0: | ||||
| 				while( dstPos < dstLen ) { | ||||
| 					for( i in 0...bmp.header.width ) { | ||||
| 						if (bmp.header.bpp == 8) { | ||||
|  | ||||
| 							var currentSrcPos = srcPos + i; | ||||
| 							var index = srcBytes.get(currentSrcPos); | ||||
| 							dstBytes.blit( dstPos, colorTable, index * COLOR_SIZE, COLOR_SIZE ); | ||||
|  | ||||
| 						} else if (bmp.header.bpp == 24) { | ||||
|  | ||||
| 							var currentSrcPos = srcPos + i * 3; | ||||
| 							var b = srcBytes.get(currentSrcPos); | ||||
| 							var g = srcBytes.get(currentSrcPos + 1); | ||||
| 							var r = srcBytes.get(currentSrcPos + 2); | ||||
| 							 | ||||
| 							dstBytes.set(dstPos + channelMap[0], alpha); | ||||
| 							dstBytes.set(dstPos + channelMap[1], r); | ||||
| 							dstBytes.set(dstPos + channelMap[2], g); | ||||
| 							dstBytes.set(dstPos + channelMap[3], b); | ||||
|  | ||||
| 						} else if (bmp.header.bpp == 32) { | ||||
|  | ||||
| 							var currentSrcPos = srcPos + i * 4; | ||||
| 							var b = srcBytes.get(currentSrcPos); | ||||
| 							var g = srcBytes.get(currentSrcPos + 1); | ||||
| 							var r = srcBytes.get(currentSrcPos + 2); | ||||
| 							 | ||||
| 							dstBytes.set(dstPos + channelMap[0], alpha); | ||||
| 							dstBytes.set(dstPos + channelMap[1], r); | ||||
| 							dstBytes.set(dstPos + channelMap[2], g); | ||||
| 							dstBytes.set(dstPos + channelMap[3], b); | ||||
|  | ||||
| 						} | ||||
| 						dstPos += 4; | ||||
| 					} | ||||
| 					srcPos += yDir * srcPaddedStride; | ||||
| 				} | ||||
| 			case 1: | ||||
| 				srcPos = 0; | ||||
| 				var x = 0; | ||||
| 				var y = bmp.header.topToBottom ? 0 : bmp.header.height - 1; | ||||
| 				while( srcPos < bmp.header.dataLength ) { | ||||
| 					var count = srcBytes.get(srcPos++); | ||||
| 					var index = srcBytes.get(srcPos++); | ||||
| 					if ( count == 0 ) { | ||||
| 						if ( index == 0 ) { | ||||
| 							x = 0; | ||||
| 							y += yDir; | ||||
| 						} else if ( index == 1 ) { | ||||
| 							break; | ||||
| 						} else if ( index == 2 ) { | ||||
| 							x += srcBytes.get(srcPos++); | ||||
| 							y += srcBytes.get(srcPos++); | ||||
| 						} else { | ||||
| 							count = index; | ||||
| 							for( i in 0...count ) { | ||||
| 								index = srcBytes.get(srcPos++); | ||||
| 								dstBytes.blit( COLOR_SIZE * ((x+i) + y * bmp.header.width), colorTable, index * COLOR_SIZE, COLOR_SIZE ); | ||||
| 							} | ||||
| 							if (srcPos % 2 != 0) srcPos++; | ||||
| 							x += count; | ||||
| 						} | ||||
| 					} else { | ||||
| 						for( i in 0...count ) { | ||||
| 							dstBytes.blit( COLOR_SIZE * ((x+i) + y * bmp.header.width), colorTable, index * COLOR_SIZE, COLOR_SIZE ); | ||||
| 						} | ||||
| 						x += count; | ||||
| 					} | ||||
| 				} | ||||
| 			default: | ||||
| 				throw 'compression ${bmp.header.compression} not supported'; | ||||
| 		} | ||||
|  | ||||
| 		return dstBytes; | ||||
| 	} | ||||
| 	 | ||||
| 	// `channelMap` contains indices to map into ARGB (f.e. the mapping for ARGB is [0,1,2,3], while for BGRA is [3,2,1,0]) | ||||
| 	static function _buildFrom32( width : Int, height : Int, srcBytes : haxe.io.Bytes, channelMap : Array<Int>, topToBottom : Bool = false ) : Data { | ||||
| 		var bpp = 24; | ||||
| 		var paddedStride = computePaddedStride(width, bpp); | ||||
| 		var bytesBGR = haxe.io.Bytes.alloc(paddedStride * height); | ||||
| 		var topToBottom = topToBottom; | ||||
| 		var dataLength = bytesBGR.length; | ||||
| 		 | ||||
| 		var dstStride = width * 3; | ||||
| 		var srcLen = width * height * 4; | ||||
| 		var yDir = -1; | ||||
| 		var dstPos = dataLength - paddedStride; | ||||
| 		var srcPos = 0; | ||||
| 		 | ||||
| 		if ( topToBottom ) { | ||||
| 			yDir = 1; | ||||
| 			dstPos = 0; | ||||
| 		} | ||||
| 		 | ||||
| 		while( srcPos < srcLen ) { | ||||
| 			var i = dstPos; | ||||
| 			while( i < dstPos + dstStride ) { | ||||
| 				var r = srcBytes.get(srcPos + channelMap[1]); | ||||
| 				var g = srcBytes.get(srcPos + channelMap[2]); | ||||
| 				var b = srcBytes.get(srcPos + channelMap[3]); | ||||
| 				 | ||||
| 				bytesBGR.set(i++, b); | ||||
| 				bytesBGR.set(i++, g); | ||||
| 				bytesBGR.set(i++, r); | ||||
| 				 | ||||
| 				srcPos += 4; | ||||
| 			} | ||||
| 			dstPos += yDir * paddedStride; | ||||
| 		} | ||||
| 		 | ||||
| 		return { | ||||
| 			header: { | ||||
| 				width: width, | ||||
| 				height: height, | ||||
| 				paddedStride: paddedStride, | ||||
| 				topToBottom: topToBottom, | ||||
| 				bpp: bpp, | ||||
| 				dataLength: dataLength, | ||||
| 				compression: 0 | ||||
| 			}, | ||||
| 			pixels: bytesBGR, | ||||
| 			colorTable: null | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										74
									
								
								leenkx/Sources/iron/format/bmp/Writer.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								leenkx/Sources/iron/format/bmp/Writer.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,74 @@ | ||||
| /* | ||||
|  * format - Haxe File Formats | ||||
|  * | ||||
|  *  BMP File Format | ||||
|  *  Copyright (C) 2007-2009 Robert Sköld | ||||
|  * | ||||
|  * Copyright (c) 2009, The Haxe Project Contributors | ||||
|  * All rights reserved. | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions are met: | ||||
|  * | ||||
|  *   - Redistributions of source code must retain the above copyright | ||||
|  *     notice, this list of conditions and the following disclaimer. | ||||
|  *   - Redistributions in binary form must reproduce the above copyright | ||||
|  *     notice, this list of conditions and the following disclaimer in the | ||||
|  *     documentation and/or other materials provided with the distribution. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY | ||||
|  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||
|  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||
|  * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR | ||||
|  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||
|  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||
|  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||
|  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||
|  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||
|  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | ||||
|  * DAMAGE. | ||||
|  */ | ||||
|  | ||||
| package iron.format.bmp; | ||||
|  | ||||
| import iron.format.bmp.Data; | ||||
|  | ||||
|  | ||||
| class Writer { | ||||
|  | ||||
| 	static var DATA_OFFSET : Int = 0x36; | ||||
|  | ||||
| 	var output : haxe.io.Output; | ||||
|  | ||||
| 	public function new(o) { | ||||
| 		output = o; | ||||
| 	} | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Specs: http://s223767089.online.de/en/file-format-bmp | ||||
| 	 */ | ||||
| 	public function write( bmp : Data ) { | ||||
| 		// Write Header (14 bytes) | ||||
| 		output.writeString( "BM" );								// Signature | ||||
| 		output.writeInt32(bmp.pixels.length + DATA_OFFSET );	// FileSize | ||||
| 		output.writeInt32( 0 );									// Reserved | ||||
| 		output.writeInt32( DATA_OFFSET );						// Offset | ||||
|  | ||||
| 		// Write InfoHeader (40 bytes) | ||||
| 		output.writeInt32( 40 );								// InfoHeader size | ||||
| 		output.writeInt32( bmp.header.width );					// Image width | ||||
| 		var height = bmp.header.height; | ||||
| 		if (bmp.header.topToBottom) height = -height;  | ||||
| 		output.writeInt32( height );							// Image height | ||||
| 		output.writeInt16( 1 );									// Number of planes | ||||
| 		output.writeInt16( 24 );								// Bits per pixel (24bit RGB) | ||||
| 		output.writeInt32( 0 );									// Compression type (no compression) | ||||
| 		output.writeInt32( bmp.header.dataLength );				// Image data size (0 when uncompressed) | ||||
| 		output.writeInt32( 0x2e30 );							// Horizontal resolution | ||||
| 		output.writeInt32( 0x2e30 );							// Vertical resolution | ||||
| 		output.writeInt32( 0 );									// Colors used (0 when uncompressed) | ||||
| 		output.writeInt32( 0 );									// Important colors (0 when uncompressed) | ||||
|  | ||||
| 		// Write Raster Data | ||||
| 		output.write(bmp.pixels); | ||||
|   } | ||||
| } | ||||
| @ -21,6 +21,7 @@ class MeshObject extends Object { | ||||
| 	public var particleChildren: Array<MeshObject> = null; | ||||
| 	public var particleOwner: MeshObject = null; // Particle object | ||||
| 	public var particleIndex = -1; | ||||
| 	public var render_emitter = true; | ||||
| 	#end | ||||
| 	public var cameraDistance: Float; | ||||
| 	public var screenSize = 0.0; | ||||
| @ -255,11 +256,11 @@ class MeshObject extends Object { | ||||
| 				particleSystems[i].update(particleChildren[i], this); | ||||
| 			} | ||||
| 		} | ||||
| 		if (particleSystems != null && particleSystems.length > 0 && !raw.render_emitter) return; | ||||
| 		if (particleSystems != null && particleSystems.length > 0 && !render_emitter) return; | ||||
|         if (particleSystems == null && cullMaterial(context)) return; | ||||
|         #else | ||||
|         if (cullMaterial(context)) return; | ||||
| 		#end | ||||
|  | ||||
| 		if (cullMaterial(context)) return; | ||||
|  | ||||
| 		// Get lod | ||||
| 		var mats = materials; | ||||
| 		var lod = this; | ||||
|  | ||||
| @ -18,29 +18,29 @@ class ParticleSystem { | ||||
| 	public var speed = 1.0; | ||||
| 	var particles: Array<Particle>; | ||||
| 	var ready: Bool; | ||||
| 	var frameRate = 24; | ||||
| 	var lifetime = 0.0; | ||||
| 	var animtime = 0.0; | ||||
| 	var time = 0.0; | ||||
| 	var spawnRate = 0.0; | ||||
| 	public var frameRate = 24; | ||||
| 	public var lifetime = 0.0; | ||||
| 	public var animtime = 0.0; | ||||
| 	public var time = 0.0; | ||||
| 	public var spawnRate = 0.0; | ||||
| 	var seed = 0; | ||||
|  | ||||
| 	var r: TParticleData; | ||||
| 	var gx: Float; | ||||
| 	var gy: Float; | ||||
| 	var gz: Float; | ||||
| 	var alignx: Float; | ||||
| 	var aligny: Float; | ||||
| 	var alignz: Float; | ||||
| 	public var r: TParticleData; | ||||
| 	public var gx: Float; | ||||
| 	public var gy: Float; | ||||
| 	public var gz: Float; | ||||
| 	public var alignx: Float; | ||||
| 	public var aligny: Float; | ||||
| 	public var alignz: Float; | ||||
| 	var dimx: Float; | ||||
| 	var dimy: Float; | ||||
| 	var tilesx: Int; | ||||
| 	var tilesy: Int; | ||||
| 	var tilesFramerate: Int; | ||||
|  | ||||
| 	var count = 0; | ||||
| 	var lap = 0; | ||||
| 	var lapTime = 0.0; | ||||
| 	public var count = 0; | ||||
| 	public var lap = 0; | ||||
| 	public var lapTime = 0.0; | ||||
| 	var m = Mat4.identity(); | ||||
|  | ||||
| 	var ownerLoc = new Vec4(); | ||||
| @ -149,7 +149,7 @@ class ParticleSystem { | ||||
| 		// GPU particles transform is attached to owner object | ||||
| 	} | ||||
|  | ||||
| 	function setupGeomGpu(object: MeshObject, owner: MeshObject) { | ||||
| 	public function setupGeomGpu(object: MeshObject, owner: MeshObject) { | ||||
| 		var instancedData = new Float32Array(particles.length * 6); | ||||
| 		var i = 0; | ||||
|  | ||||
|  | ||||
							
								
								
									
										99
									
								
								leenkx/Sources/leenkx/logicnode/AddParticleToObjectNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								leenkx/Sources/leenkx/logicnode/AddParticleToObjectNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,99 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import iron.data.SceneFormat.TSceneFormat; | ||||
| import iron.data.Data; | ||||
| import iron.object.Object; | ||||
|  | ||||
| class AddParticleToObjectNode extends LogicNode { | ||||
|  | ||||
| 	public var property0: String; | ||||
|  | ||||
| 	public function new(tree: LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function run(from: Int) { | ||||
| 		#if lnx_particles | ||||
|  | ||||
| 		if (property0 == 'Scene Active'){		 | ||||
| 			var objFrom: Object = inputs[1].get(); | ||||
| 			var slot: Int = inputs[2].get(); | ||||
| 			var objTo: Object = inputs[3].get(); | ||||
| 	 | ||||
| 			if (objFrom == null || objTo == null) return; | ||||
| 	 | ||||
| 			var mobjFrom = cast(objFrom, iron.object.MeshObject); | ||||
| 	 | ||||
| 			var psys = mobjFrom.particleSystems != null ? mobjFrom.particleSystems[slot] :  | ||||
| 				mobjFrom.particleOwner != null && mobjFrom.particleOwner.particleSystems != null ? mobjFrom.particleOwner.particleSystems[slot] : null; | ||||
|  | ||||
| 			if (psys == null) return; | ||||
| 	 | ||||
| 			var mobjTo = cast(objTo, iron.object.MeshObject); | ||||
| 	 | ||||
| 			mobjTo.setupParticleSystem(iron.Scene.active.raw.name, {name: 'LnxPS', seed: 0, particle: psys.r.name}); | ||||
| 			 | ||||
| 			mobjTo.render_emitter = inputs[4].get(); | ||||
|  | ||||
| 			iron.Scene.active.spawnObject(psys.data.raw.instance_object, null, function(o: Object) { | ||||
| 				if (o != null) { | ||||
| 					var c: iron.object.MeshObject = cast o; | ||||
| 					if (mobjTo.particleChildren == null) mobjTo.particleChildren = []; | ||||
| 					mobjTo.particleChildren.push(c); | ||||
| 					c.particleOwner = mobjTo; | ||||
| 					c.particleIndex = mobjTo.particleChildren.length - 1; | ||||
| 				} | ||||
| 			}); | ||||
| 	 | ||||
| 			var oslot: Int = mobjTo.particleSystems.length-1; | ||||
| 			var opsys = mobjTo.particleSystems[oslot]; | ||||
| 			opsys.setupGeomGpu(mobjTo.particleChildren[oslot], mobjTo); | ||||
| 		 | ||||
| 		} else { | ||||
| 			var sceneName: String = inputs[1].get(); | ||||
| 			var objectName: String = inputs[2].get(); | ||||
| 			var slot: Int = inputs[3].get(); | ||||
|  | ||||
| 			var mobjTo: Object = inputs[4].get(); | ||||
| 			var mobjTo = cast(mobjTo, iron.object.MeshObject); | ||||
|  | ||||
| 			#if lnx_json | ||||
| 				sceneName += ".json"; | ||||
| 			#elseif lnx_compress | ||||
| 				sceneName += ".lz4"; | ||||
| 			#end | ||||
|  | ||||
| 			Data.getSceneRaw(sceneName, (rawScene: TSceneFormat) -> { | ||||
|  | ||||
| 			for (obj in rawScene.objects) { | ||||
| 				if (obj.name == objectName) { | ||||
| 					mobjTo.setupParticleSystem(sceneName, obj.particle_refs[slot]); | ||||
| 					mobjTo.render_emitter = inputs[5].get();					 | ||||
|  | ||||
| 					iron.Scene.active.spawnObject(rawScene.particle_datas[slot].instance_object, null, function(o: Object) { | ||||
| 						if (o != null) { | ||||
| 							var c: iron.object.MeshObject = cast o; | ||||
| 							if (mobjTo.particleChildren == null) mobjTo.particleChildren = []; | ||||
| 							mobjTo.particleChildren.push(c); | ||||
| 							c.particleOwner = mobjTo; | ||||
| 							c.particleIndex = mobjTo.particleChildren.length - 1; | ||||
| 						} | ||||
| 					}, true, rawScene); | ||||
|  | ||||
| 					var oslot: Int = mobjTo.particleSystems.length-1; | ||||
| 					var opsys = mobjTo.particleSystems[oslot]; | ||||
| 					opsys.setupGeomGpu(mobjTo.particleChildren[oslot], mobjTo); | ||||
|  | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			}); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		#end | ||||
|  | ||||
| 		runOutput(0); | ||||
| 	} | ||||
| } | ||||
| @ -2,6 +2,9 @@ package leenkx.logicnode; | ||||
|  | ||||
| import iron.Scene; | ||||
| import iron.object.CameraObject; | ||||
| import iron.math.Vec4; | ||||
| import iron.math.Quat; | ||||
| import leenkx.math.Helper; | ||||
|  | ||||
| import leenkx.renderpath.RenderPathCreator; | ||||
|  | ||||
| @ -27,11 +30,19 @@ class DrawCameraTextureNode extends LogicNode { | ||||
| 				final c = inputs[2].get(); | ||||
| 				assert(Error, Std.isOfType(c, CameraObject), "Camera must be a camera object!"); | ||||
| 				cam = cast(c, CameraObject); | ||||
| 				rt = kha.Image.createRenderTarget(iron.App.w(), iron.App.h()); | ||||
| 				rt = kha.Image.createRenderTarget(iron.App.w(), iron.App.h(), | ||||
| 					kha.graphics4.TextureFormat.RGBA32, | ||||
| 					kha.graphics4.DepthStencilFormat.NoDepthAndStencil); | ||||
|  | ||||
| 				assert(Error, mo.materials[matSlot].contexts[0].textures != null, 'Object "${mo.name}" has no diffuse texture to render to'); | ||||
| 				mo.materials[matSlot].contexts[0].textures[0] = rt; // Override diffuse texture | ||||
|  | ||||
|                 final n = inputs[5].get(); | ||||
| 				for (i => node in mo.materials[matSlot].contexts[0].raw.bind_textures){ | ||||
| 					if (node.name == n){ | ||||
| 						mo.materials[matSlot].contexts[0].textures[i] = rt; // Override diffuse texture | ||||
| 						break; | ||||
| 					} | ||||
| 				} | ||||
| 				tree.notifyOnRender(render); | ||||
| 				runOutput(0); | ||||
|  | ||||
| @ -48,8 +59,20 @@ class DrawCameraTextureNode extends LogicNode { | ||||
| 		iron.Scene.active.camera = cam; | ||||
| 		cam.renderTarget = rt; | ||||
|  | ||||
| 		#if kha_html5 | ||||
| 		var q: Quat = new Quat(); | ||||
| 		q.fromAxisAngle(new Vec4(0, 0, 1, 1), Helper.degToRad(180)); | ||||
| 		cam.transform.rot.mult(q); | ||||
| 		cam.transform.buildMatrix(); | ||||
| 		#end | ||||
|  | ||||
| 		cam.renderFrame(g); | ||||
|  | ||||
| 		#if kha_html5 | ||||
| 		cam.transform.rot.mult(q); | ||||
| 		cam.transform.buildMatrix(); | ||||
| 		#end | ||||
|  | ||||
| 		cam.renderTarget = oldRT; | ||||
| 		iron.Scene.active.camera = sceneCam; | ||||
| 	} | ||||
|  | ||||
| @ -99,8 +99,6 @@ class DrawImageSequenceNode extends LogicNode { | ||||
| 		final colorVec = inputs[4].get(); | ||||
| 		g.color = Color.fromFloats(colorVec.x, colorVec.y, colorVec.z, colorVec.w); | ||||
|  | ||||
| 		trace(currentImgIdx); | ||||
|  | ||||
| 		g.drawScaledImage(images[currentImgIdx], inputs[5].get(), inputs[6].get(), inputs[7].get(), inputs[8].get()); | ||||
| 	} | ||||
| } | ||||
|  | ||||
							
								
								
									
										59
									
								
								leenkx/Sources/leenkx/logicnode/DrawSubImageNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								leenkx/Sources/leenkx/logicnode/DrawSubImageNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,59 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import iron.math.Vec4; | ||||
| import kha.Image; | ||||
| import kha.Color; | ||||
| import leenkx.renderpath.RenderToTexture; | ||||
|  | ||||
| class DrawSubImageNode extends LogicNode { | ||||
|     var img: Image; | ||||
|     var lastImgName = ""; | ||||
|  | ||||
|     public function new(tree: LogicTree) { | ||||
|         super(tree); | ||||
|     } | ||||
|  | ||||
|     override function run(from: Int) { | ||||
|         RenderToTexture.ensure2DContext("DrawImageNode"); | ||||
|  | ||||
|         final imgName: String = inputs[1].get(); | ||||
|         final colorVec: Vec4 = inputs[2].get(); | ||||
|         final anchorH: Int = inputs[3].get(); | ||||
|         final anchorV: Int = inputs[4].get(); | ||||
|         final x: Float = inputs[5].get(); | ||||
|         final y: Float = inputs[6].get(); | ||||
|         final width: Float = inputs[7].get(); | ||||
|         final height: Float = inputs[8].get(); | ||||
|         final sx: Float = inputs[9].get(); | ||||
|         final sy: Float = inputs[10].get(); | ||||
|         final swidth: Float = inputs[11].get(); | ||||
|         final sheight: Float = inputs[12].get(); | ||||
|         final angle: Float = inputs[13].get(); | ||||
|  | ||||
|         final drawx = x - 0.5 * width * anchorH; | ||||
|         final drawy = y - 0.5 * height * anchorV; | ||||
|         final sdrawx = sx - 0.5 * swidth * anchorH; | ||||
|         final sdrawy = sy - 0.5 * sheight * anchorV; | ||||
|  | ||||
|         RenderToTexture.g.rotate(angle, x, y); | ||||
|  | ||||
|         if (imgName != lastImgName) { | ||||
|             // Load new image | ||||
|             lastImgName = imgName; | ||||
|             iron.data.Data.getImage(imgName, (image: Image) -> { | ||||
|                 img = image; | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         if (img == null) { | ||||
|             runOutput(0); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         RenderToTexture.g.color = Color.fromFloats(colorVec.x, colorVec.y, colorVec.z, colorVec.w); | ||||
|         RenderToTexture.g.drawScaledSubImage(img, sdrawx, sdrawy, swidth, sheight, drawx, drawy, width, height); | ||||
|         RenderToTexture.g.rotate(-angle, x, y); | ||||
|  | ||||
|         runOutput(0); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										66
									
								
								leenkx/Sources/leenkx/logicnode/GetParticleDataNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								leenkx/Sources/leenkx/logicnode/GetParticleDataNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,66 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import iron.object.Object; | ||||
|  | ||||
| class GetParticleDataNode extends LogicNode { | ||||
|  | ||||
| 	public function new(tree: LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function get(from: Int): Dynamic { | ||||
| 		var object: Object = inputs[0].get(); | ||||
| 		var slot: Int = inputs[1].get(); | ||||
|  | ||||
| 		if (object == null) return null; | ||||
|  | ||||
| 	#if lnx_particles | ||||
|  | ||||
| 		var mo = cast(object, iron.object.MeshObject); | ||||
|  | ||||
| 		var psys = mo.particleSystems != null ? mo.particleSystems[slot] :  | ||||
| 			mo.particleOwner != null && mo.particleOwner.particleSystems != null ? mo.particleOwner.particleSystems[slot] : null; | ||||
|  | ||||
| 		if (psys == null) return null; | ||||
|  | ||||
| 		return switch (from) { | ||||
| 			case 0: | ||||
| 				psys.r.name; | ||||
| 			case 1: | ||||
| 				psys.r.particle_size; | ||||
| 			case 2: | ||||
| 				psys.r.frame_start; | ||||
| 			case 3: | ||||
| 				psys.r.frame_end; | ||||
| 			case 4: | ||||
| 				psys.lifetime; | ||||
| 			case 5: | ||||
| 				psys.r.lifetime; | ||||
| 			case 6: | ||||
| 				psys.r.emit_from; | ||||
| 			case 7: | ||||
| 				new iron.math.Vec3(psys.alignx*2, psys.aligny*2, psys.alignz*2); | ||||
| 			case 8: | ||||
| 				psys.r.factor_random; | ||||
| 			case 9: | ||||
| 				new iron.math.Vec3(psys.gx, psys.gy, psys.gz); | ||||
| 			case 10: | ||||
| 				psys.r.weight_gravity; | ||||
| 			case 11: | ||||
| 				psys.speed; | ||||
| 			case 12: | ||||
| 				psys.time; | ||||
| 			case 13: | ||||
| 				psys.lap; | ||||
| 			case 14: | ||||
| 				psys.lapTime; | ||||
| 			case 15: | ||||
| 				psys.count; | ||||
| 			default:  | ||||
| 				null; | ||||
| 		} | ||||
| 	#end | ||||
|  | ||||
| 		return null; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										38
									
								
								leenkx/Sources/leenkx/logicnode/GetParticleNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								leenkx/Sources/leenkx/logicnode/GetParticleNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import iron.object.Object; | ||||
|  | ||||
| class GetParticleNode extends LogicNode { | ||||
|  | ||||
| 	public function new(tree: LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function get(from: Int): Dynamic { | ||||
| 		var object: Object = inputs[0].get(); | ||||
|  | ||||
| 		if (object == null) return null; | ||||
|  | ||||
| 	#if lnx_particles | ||||
|  | ||||
| 		var mo = cast(object, iron.object.MeshObject); | ||||
| 	 | ||||
| 		switch (from) { | ||||
| 			case 0: | ||||
| 				var names: Array<String> = []; | ||||
| 				if (mo.particleSystems != null) | ||||
| 					for (psys in mo.particleSystems) | ||||
| 						names.push(psys.r.name); | ||||
| 				return names; | ||||
| 			case 1: | ||||
| 				return mo.particleSystems != null ? mo.particleSystems.length : 0; | ||||
| 			case 2: | ||||
| 				return mo.render_emitter; | ||||
| 			default:  | ||||
| 				null; | ||||
| 		} | ||||
| 	#end | ||||
|  | ||||
| 		return null; | ||||
| 	} | ||||
| } | ||||
| @ -18,7 +18,6 @@ class ProbabilisticOutputNode extends LogicNode { | ||||
| 		} | ||||
|  | ||||
| 		if (sum > 1){ | ||||
| 			trace(sum); | ||||
| 			for (p in 0...probs.length) | ||||
| 				probs[p] /= sum; | ||||
| 		} | ||||
|  | ||||
| @ -0,0 +1,64 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import iron.object.Object; | ||||
|  | ||||
| class RemoveParticleFromObjectNode extends LogicNode { | ||||
|  | ||||
| 	public var property0: String; | ||||
|  | ||||
| 	public function new(tree: LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function run(from: Int) { | ||||
| 		#if lnx_particles | ||||
| 		var object: Object = inputs[1].get(); | ||||
|  | ||||
| 		if (object == null) return; | ||||
|  | ||||
| 		var mo = cast(object, iron.object.MeshObject); | ||||
|  | ||||
| 		if (mo.particleSystems == null) return; | ||||
|  | ||||
| 		if (property0 == 'All'){ | ||||
| 			mo.particleSystems = null; | ||||
| 			for (c in mo.particleChildren) c.remove(); | ||||
| 			mo.particleChildren = null; | ||||
| 			mo.particleOwner = null; | ||||
| 			mo.render_emitter = true; | ||||
| 		}  | ||||
| 		else { | ||||
| 			 | ||||
| 			var slot: Int = -1; | ||||
| 			if (property0 == 'Name'){ | ||||
| 				var name: String = inputs[2].get(); | ||||
| 				for (i => psys in mo.particleSystems){ | ||||
| 					if (psys.r.name == name){ slot = i; break; } | ||||
| 				} | ||||
| 			}  | ||||
| 			else slot = inputs[2].get(); | ||||
| 				 | ||||
| 			if (mo.particleSystems.length > slot){ | ||||
| 				for (i in slot+1...mo.particleSystems.length){ | ||||
| 					var mi = cast(mo.particleChildren[i], iron.object.MeshObject); | ||||
| 					mi.particleIndex = mi.particleIndex - 1; | ||||
| 				} | ||||
| 				mo.particleSystems.splice(slot, 1); | ||||
| 				mo.particleChildren[slot].remove(); | ||||
| 				mo.particleChildren.splice(slot, 1); | ||||
| 			} | ||||
|  | ||||
| 			if (slot == 0){ | ||||
| 				mo.particleSystems = null; | ||||
| 				mo.particleChildren = null; | ||||
| 				mo.particleOwner = null; | ||||
| 				mo.render_emitter = true; | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		#end | ||||
|  | ||||
| 		runOutput(0); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										75
									
								
								leenkx/Sources/leenkx/logicnode/SetParticleDataNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								leenkx/Sources/leenkx/logicnode/SetParticleDataNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,75 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import iron.object.Object; | ||||
|  | ||||
| class SetParticleDataNode extends LogicNode { | ||||
|  | ||||
| 	public var property0: String; | ||||
|  | ||||
| 	public function new(tree: LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function run(from: Int) { | ||||
| 		#if lnx_particles | ||||
| 		var object: Object = inputs[1].get(); | ||||
| 		var slot: Int = inputs[2].get(); | ||||
|  | ||||
| 		if (object == null) return; | ||||
|  | ||||
| 		var mo = cast(object, iron.object.MeshObject); | ||||
|  | ||||
| 		var psys = mo.particleSystems != null ? mo.particleSystems[slot] :  | ||||
| 			mo.particleOwner != null && mo.particleOwner.particleSystems != null ? mo.particleOwner.particleSystems[slot] : null;		if (psys == null) return; | ||||
|  | ||||
| 		switch (property0) { | ||||
| 			case 'Particle Size': | ||||
| 				psys.r.particle_size = inputs[3].get(); | ||||
| 			case 'Frame Start': | ||||
| 				psys.r.frame_start = inputs[3].get(); | ||||
| 				psys.animtime = (psys.r.frame_end - psys.r.frame_start) / psys.frameRate; | ||||
| 				psys.spawnRate = ((psys.r.frame_end - psys.r.frame_start) / psys.count) / psys.frameRate; | ||||
| 			case 'Frame End': | ||||
| 				psys.r.frame_end = inputs[3].get(); | ||||
| 				psys.animtime = (psys.r.frame_end - psys.r.frame_start) / psys.frameRate; | ||||
| 				psys.spawnRate = ((psys.r.frame_end - psys.r.frame_start) / psys.count) / psys.frameRate; | ||||
| 			case 'Lifetime': | ||||
| 				psys.lifetime = inputs[3].get() / psys.frameRate; | ||||
| 			case 'Lifetime Random': | ||||
| 				psys.r.lifetime_random = inputs[3].get(); | ||||
| 			case 'Emit From': | ||||
| 				var emit_from: Int = inputs[3].get(); | ||||
| 				if (emit_from == 0 || emit_from == 1 || emit_from == 2) { | ||||
| 					psys.r.emit_from = emit_from; | ||||
| 					psys.setupGeomGpu(mo.particleChildren != null ? mo.particleChildren[slot] : cast(iron.Scene.active.getChild(psys.data.raw.instance_object), iron.object.MeshObject), mo); | ||||
| 				} | ||||
| 			case 'Velocity': | ||||
| 				var vel: iron.math.Vec3 = inputs[3].get(); | ||||
| 				psys.alignx = vel.x / 2; | ||||
| 				psys.aligny = vel.y / 2; | ||||
| 				psys.alignz = vel.z / 2; | ||||
| 			case 'Velocity Random': | ||||
| 				psys.r.factor_random = inputs[3].get(); | ||||
| 			case 'Weight Gravity': | ||||
| 				psys.r.weight_gravity = inputs[3].get(); | ||||
| 				if (iron.Scene.active.raw.gravity != null) { | ||||
| 					psys.gx = iron.Scene.active.raw.gravity[0] * psys.r.weight_gravity; | ||||
| 					psys.gy = iron.Scene.active.raw.gravity[1] * psys.r.weight_gravity; | ||||
| 					psys.gz = iron.Scene.active.raw.gravity[2] * psys.r.weight_gravity; | ||||
| 				} | ||||
| 				else { | ||||
| 					psys.gx = 0; | ||||
| 					psys.gy = 0; | ||||
| 					psys.gz = -9.81 * psys.r.weight_gravity; | ||||
| 				} | ||||
| 			case 'Speed': | ||||
| 				psys.speed = inputs[3].get(); | ||||
| 			default:  | ||||
| 				null; | ||||
| 		} | ||||
|  | ||||
| 		#end | ||||
|  | ||||
| 		runOutput(0); | ||||
| 	} | ||||
| } | ||||
| @ -0,0 +1,23 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import iron.object.Object; | ||||
|  | ||||
| class SetParticleRenderEmitterNode extends LogicNode { | ||||
|  | ||||
| 	public function new(tree: LogicTree) { | ||||
| 		super(tree); | ||||
| 	} | ||||
|  | ||||
| 	override function run(from: Int) { | ||||
| 		#if lnx_particles | ||||
| 		var object: Object = inputs[1].get(); | ||||
|  | ||||
| 		if (object == null) return; | ||||
|  | ||||
| 		cast(object, iron.object.MeshObject).render_emitter = inputs[2].get(); | ||||
|  | ||||
| 		#end | ||||
|  | ||||
| 		runOutput(0); | ||||
| 	} | ||||
| } | ||||
| @ -11,13 +11,16 @@ class SetParticleSpeedNode extends LogicNode { | ||||
| 	override function run(from: Int) { | ||||
| 		#if lnx_particles | ||||
| 		var object: Object = inputs[1].get(); | ||||
| 		var speed: Float = inputs[2].get(); | ||||
| 		var slot: Int = inputs[2].get(); | ||||
| 		var speed: Float = inputs[3].get(); | ||||
|  | ||||
| 		if (object == null) return; | ||||
|  | ||||
| 		var mo = cast(object, iron.object.MeshObject); | ||||
| 		var psys = mo.particleSystems.length > 0 ? mo.particleSystems[0] : null; | ||||
| 		if (psys == null) mo.particleOwner.particleSystems[0]; | ||||
| 		var psys = mo.particleSystems != null ? mo.particleSystems[slot] :  | ||||
| 			mo.particleOwner != null && mo.particleOwner.particleSystems != null ? mo.particleOwner.particleSystems[slot] : null; | ||||
|  | ||||
| 		if (psys == null) return; | ||||
|  | ||||
| 		psys.speed = speed; | ||||
|  | ||||
|  | ||||
							
								
								
									
										105
									
								
								leenkx/Sources/leenkx/logicnode/WriteImageNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								leenkx/Sources/leenkx/logicnode/WriteImageNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,105 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import iron.object.CameraObject; | ||||
|  | ||||
| class WriteImageNode extends LogicNode { | ||||
|  | ||||
|     var file: String; | ||||
|     var camera: CameraObject; | ||||
|     var renderTarget: kha.Image; | ||||
|  | ||||
|     public function new(tree: LogicTree) { | ||||
|         super(tree); | ||||
|     } | ||||
|  | ||||
|     override function run(from: Int) { | ||||
|         // Relative or absolute path to file | ||||
|         file = inputs[1].get(); | ||||
|  | ||||
|         assert(Error, iron.App.w() % inputs[3].get() == 0 && iron.App.h() % inputs[4].get() == 0, "Aspect ratio must match display resolution ratio"); | ||||
|  | ||||
|         camera = inputs[2].get(); | ||||
|         renderTarget = kha.Image.createRenderTarget(inputs[3].get(), inputs[4].get(), | ||||
|             kha.graphics4.TextureFormat.RGBA32, | ||||
|             kha.graphics4.DepthStencilFormat.NoDepthAndStencil); | ||||
|  | ||||
|         tree.notifyOnRender(render); | ||||
|          | ||||
|     } | ||||
|  | ||||
|     function render(g: kha.graphics4.Graphics) { | ||||
|  | ||||
|         var ready = false; | ||||
|         final sceneCam = iron.Scene.active.camera; | ||||
|         final oldRT = camera.renderTarget; | ||||
|  | ||||
|         iron.Scene.active.camera = camera; | ||||
|         camera.renderTarget = renderTarget; | ||||
|  | ||||
|         camera.renderFrame(g); | ||||
|  | ||||
|         var tex = camera.renderTarget; | ||||
|  | ||||
|         camera.renderTarget = oldRT; | ||||
|         iron.Scene.active.camera = sceneCam; | ||||
|  | ||||
|         var pixels = tex.getPixels(); | ||||
|  | ||||
|         for (i in 0...pixels.length){ | ||||
|             if (pixels.get(i) != 0){ ready = true; break; } | ||||
|         } | ||||
|  | ||||
|         //wait for getPixels ready | ||||
|         if (ready) {  | ||||
|  | ||||
|             var tx = inputs[5].get(); | ||||
|             var ty = inputs[6].get(); | ||||
|             var tw = inputs[7].get(); | ||||
|             var th = inputs[8].get(); | ||||
|  | ||||
|             var bo = new haxe.io.BytesOutput(); | ||||
|             var rgb = haxe.io.Bytes.alloc(tw * th * 4); | ||||
|             for (j in ty...ty + th) { | ||||
|                 for (i in tx...tx + tw) { | ||||
|                     var k = j * tex.width + i; | ||||
|                     var m =  (j - ty) * tw + i - tx; | ||||
|                      | ||||
|                     #if kha_krom | ||||
|                     var l = k; | ||||
|                     #elseif kha_html5 | ||||
|                     var l = (tex.height - j) * tex.width + i; | ||||
|                     #end | ||||
|  | ||||
|                     //ARGB 0xff | ||||
|                     rgb.set(m * 4 + 0, pixels.get(l * 4 + 3)); | ||||
|                     rgb.set(m * 4 + 1, pixels.get(l * 4 + 0));  | ||||
|                     rgb.set(m * 4 + 2, pixels.get(l * 4 + 1)); | ||||
|                     rgb.set(m * 4 + 3, pixels.get(l * 4 + 2)); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             var imgwriter = new iron.format.bmp.Writer(bo); | ||||
|             imgwriter.write(iron.format.bmp.Tools.buildFromARGB(tw, th, rgb)); | ||||
|  | ||||
|             #if kha_krom | ||||
|             Krom.fileSaveBytes(Krom.getFilesLocation() +  "/" + file, bo.getBytes().getData()); | ||||
|      | ||||
|             #elseif kha_html5 | ||||
|             var blob = new js.html.Blob([bo.getBytes().getData()], {type: "application"}); | ||||
|             var url = js.html.URL.createObjectURL(blob); | ||||
|             var a = cast(js.Browser.document.createElement("a"), js.html.AnchorElement); | ||||
|             a.href = url; | ||||
|             a.download = file; | ||||
|             a.click(); | ||||
|             js.html.URL.revokeObjectURL(url); | ||||
|             #end | ||||
|  | ||||
|             runOutput(0); | ||||
|  | ||||
|             tree.removeRender(render); | ||||
|  | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
		Reference in New Issue
	
	Block a user