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