- [3D Mesh] Fix rotation manipulation error with in euler angles unit.

This commit is contained in:
Tanasart 2024-05-01 19:28:15 +07:00
parent be111dc314
commit c751d80542
67 changed files with 9791 additions and 532 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
// 2024-04-29 18:32:52 // 2024-05-01 08:45:28
#event properties (no comments/etc. here are saved) #event properties (no comments/etc. here are saved)
parent_index = -1; parent_index = -1;
persistent = true; persistent = true;
@ -1378,110 +1378,112 @@ if(APP_SURF_OVERRIDE) { #region
if(winMan_isMinimized()) exit; if(winMan_isMinimized()) exit;
#region tooltip #region tooltip
if(is_struct(TOOLTIP)) { if(!_MOUSE_BLOCK) {
if(struct_has(TOOLTIP, "drawTooltip")) if(is_struct(TOOLTIP)) {
TOOLTIP.drawTooltip(); if(struct_has(TOOLTIP, "drawTooltip"))
TOOLTIP.drawTooltip();
} else if(is_array(TOOLTIP)) {
var content = TOOLTIP[0];
var type = TOOLTIP[1];
if(is_method(content)) content = content();
switch(type) {
case VALUE_TYPE.float :
case VALUE_TYPE.integer :
case VALUE_TYPE.text :
case VALUE_TYPE.struct :
case VALUE_TYPE.path :
draw_tooltip_text(string_real(content));
break;
case VALUE_TYPE.boolean : } else if(is_array(TOOLTIP)) {
draw_tooltip_text(printBool(content)); var content = TOOLTIP[0];
break; var type = TOOLTIP[1];
case VALUE_TYPE.curve : if(is_method(content)) content = content();
draw_tooltip_text("[" + __txt("Curve Object") + "]");
break;
case VALUE_TYPE.color : switch(type) {
draw_tooltip_color(content); case VALUE_TYPE.float :
break; case VALUE_TYPE.integer :
case VALUE_TYPE.text :
case VALUE_TYPE.gradient : case VALUE_TYPE.struct :
draw_tooltip_gradient(content); case VALUE_TYPE.path :
break; draw_tooltip_text(string_real(content));
break;
case VALUE_TYPE.d3object :
draw_tooltip_text("[" + __txt("3D Object") + "]"); case VALUE_TYPE.boolean :
break; draw_tooltip_text(printBool(content));
break;
case VALUE_TYPE.object :
draw_tooltip_text("[" + __txt("Object") + "]"); case VALUE_TYPE.curve :
break; draw_tooltip_text("[" + __txt("Curve Object") + "]");
break;
case VALUE_TYPE.surface :
draw_tooltip_surface(content); case VALUE_TYPE.color :
break; draw_tooltip_color(content);
break;
case VALUE_TYPE.rigid :
draw_tooltip_text("[" + __txt("Rigidbody Object") + " (id: " + string(content[$ "object"]) + ")]"); case VALUE_TYPE.gradient :
break; draw_tooltip_gradient(content);
break;
case VALUE_TYPE.particle :
var txt = "[" + case VALUE_TYPE.d3object :
__txt("Particle Object") + draw_tooltip_text("[" + __txt("3D Object") + "]");
" (size: " + string(array_length(content)) + ") " + break;
"]";
draw_tooltip_text(txt); case VALUE_TYPE.object :
break; draw_tooltip_text("[" + __txt("Object") + "]");
break;
case VALUE_TYPE.pathnode :
draw_tooltip_text("[" + __txt("Path Object") + "]"); case VALUE_TYPE.surface :
break; draw_tooltip_surface(content);
break;
case VALUE_TYPE.sdomain :
draw_tooltip_text("[" + __txt("Domain") + " (id: " + string(content) + ")]"); case VALUE_TYPE.rigid :
break; draw_tooltip_text("[" + __txt("Rigidbody Object") + " (id: " + string(content[$ "object"]) + ")]");
break;
case VALUE_TYPE.strands :
var txt = __txt("Strands Object"); case VALUE_TYPE.particle :
if(is_struct(content)) var txt = "[" +
txt += " (strands: " + string(array_length(content.hairs)) + ")"; __txt("Particle Object") +
draw_tooltip_text("[" + txt + "]"); " (size: " + string(array_length(content)) + ") " +
break; "]";
draw_tooltip_text(txt);
case VALUE_TYPE.mesh : break;
var txt = __txt("Mesh Object");
if(is_struct(content)) case VALUE_TYPE.pathnode :
txt += " (triangles: " + string(array_length(content.triangles)) + ")"; draw_tooltip_text("[" + __txt("Path Object") + "]");
draw_tooltip_text("[" + txt + "]"); break;
break;
case VALUE_TYPE.sdomain :
case VALUE_TYPE.d3vertex : draw_tooltip_text("[" + __txt("Domain") + " (id: " + string(content) + ")]");
var txt = __txt("3D Vertex"); break;
txt += " (groups: " + string(array_length(content)) + ")";
draw_tooltip_text("[" + txt + "]"); case VALUE_TYPE.strands :
break; var txt = __txt("Strands Object");
if(is_struct(content))
case VALUE_TYPE.buffer : txt += " (strands: " + string(array_length(content.hairs)) + ")";
draw_tooltip_buffer(content); draw_tooltip_text("[" + txt + "]");
break; break;
case "sprite" : case VALUE_TYPE.mesh :
draw_tooltip_sprite(content); var txt = __txt("Mesh Object");
break; if(is_struct(content))
txt += " (triangles: " + string(array_length(content.triangles)) + ")";
default : draw_tooltip_text("[" + txt + "]");
var tt = ""; break;
if(is_struct(content)) tt = $"[{instanceof(content)}] {content}";
else tt = string(content); case VALUE_TYPE.d3vertex :
var txt = __txt("3D Vertex");
draw_tooltip_text(tt); txt += " (groups: " + string(array_length(content)) + ")";
} draw_tooltip_text("[" + txt + "]");
} else if(TOOLTIP != "") break;
draw_tooltip_text(TOOLTIP);
case VALUE_TYPE.buffer :
draw_tooltip_buffer(content);
break;
case "sprite" :
draw_tooltip_sprite(content);
break;
default :
var tt = "";
if(is_struct(content)) tt = $"[{instanceof(content)}] {content}";
else tt = string(content);
draw_tooltip_text(tt);
}
} else if(TOOLTIP != "")
draw_tooltip_text(TOOLTIP);
}
TOOLTIP = ""; TOOLTIP = "";
#endregion #endregion

View file

@ -1,4 +1,4 @@
// 2024-04-21 15:26:33 // 2024-05-01 08:44:52
#event properties (no comments/etc. here are saved) #event properties (no comments/etc. here are saved)
parent_index = -1; parent_index = -1;
persistent = true; persistent = true;
@ -1378,110 +1378,112 @@ if(APP_SURF_OVERRIDE) { #region
if(winMan_isMinimized()) exit; if(winMan_isMinimized()) exit;
#region tooltip #region tooltip
if(is_struct(TOOLTIP)) { if(!_MOUSE_BLOCK) {
if(struct_has(TOOLTIP, "drawTooltip")) if(is_struct(TOOLTIP)) {
TOOLTIP.drawTooltip(); if(struct_has(TOOLTIP, "drawTooltip"))
TOOLTIP.drawTooltip();
} else if(is_array(TOOLTIP)) {
var content = TOOLTIP[0];
var type = TOOLTIP[1];
if(is_method(content)) content = content();
switch(type) {
case VALUE_TYPE.float :
case VALUE_TYPE.integer :
case VALUE_TYPE.text :
case VALUE_TYPE.struct :
case VALUE_TYPE.path :
draw_tooltip_text(string_real(content));
break;
case VALUE_TYPE.boolean : } else if(is_array(TOOLTIP)) {
draw_tooltip_text(printBool(content)); var content = TOOLTIP[0];
break; var type = TOOLTIP[1];
case VALUE_TYPE.curve : if(is_method(content)) content = content();
draw_tooltip_text("[" + __txt("Curve Object") + "]");
break;
case VALUE_TYPE.color : switch(type) {
draw_tooltip_color(content); case VALUE_TYPE.float :
break; case VALUE_TYPE.integer :
case VALUE_TYPE.text :
case VALUE_TYPE.gradient : case VALUE_TYPE.struct :
draw_tooltip_gradient(content); case VALUE_TYPE.path :
break; draw_tooltip_text(string_real(content));
break;
case VALUE_TYPE.d3object :
draw_tooltip_text("[" + __txt("3D Object") + "]"); case VALUE_TYPE.boolean :
break; draw_tooltip_text(printBool(content));
break;
case VALUE_TYPE.object :
draw_tooltip_text("[" + __txt("Object") + "]"); case VALUE_TYPE.curve :
break; draw_tooltip_text("[" + __txt("Curve Object") + "]");
break;
case VALUE_TYPE.surface :
draw_tooltip_surface(content); case VALUE_TYPE.color :
break; draw_tooltip_color(content);
break;
case VALUE_TYPE.rigid :
draw_tooltip_text("[" + __txt("Rigidbody Object") + " (id: " + string(content[$ "object"]) + ")]"); case VALUE_TYPE.gradient :
break; draw_tooltip_gradient(content);
break;
case VALUE_TYPE.particle :
var txt = "[" + case VALUE_TYPE.d3object :
__txt("Particle Object") + draw_tooltip_text("[" + __txt("3D Object") + "]");
" (size: " + string(array_length(content)) + ") " + break;
"]";
draw_tooltip_text(txt); case VALUE_TYPE.object :
break; draw_tooltip_text("[" + __txt("Object") + "]");
break;
case VALUE_TYPE.pathnode :
draw_tooltip_text("[" + __txt("Path Object") + "]"); case VALUE_TYPE.surface :
break; draw_tooltip_surface(content);
break;
case VALUE_TYPE.sdomain :
draw_tooltip_text("[" + __txt("Domain") + " (id: " + string(content) + ")]"); case VALUE_TYPE.rigid :
break; draw_tooltip_text("[" + __txt("Rigidbody Object") + " (id: " + string(content[$ "object"]) + ")]");
break;
case VALUE_TYPE.strands :
var txt = __txt("Strands Object"); case VALUE_TYPE.particle :
if(is_struct(content)) var txt = "[" +
txt += " (strands: " + string(array_length(content.hairs)) + ")"; __txt("Particle Object") +
draw_tooltip_text("[" + txt + "]"); " (size: " + string(array_length(content)) + ") " +
break; "]";
draw_tooltip_text(txt);
case VALUE_TYPE.mesh : break;
var txt = __txt("Mesh Object");
if(is_struct(content)) case VALUE_TYPE.pathnode :
txt += " (triangles: " + string(array_length(content.triangles)) + ")"; draw_tooltip_text("[" + __txt("Path Object") + "]");
draw_tooltip_text("[" + txt + "]"); break;
break;
case VALUE_TYPE.sdomain :
case VALUE_TYPE.d3vertex : draw_tooltip_text("[" + __txt("Domain") + " (id: " + string(content) + ")]");
var txt = __txt("3D Vertex"); break;
txt += " (groups: " + string(array_length(content)) + ")";
draw_tooltip_text("[" + txt + "]"); case VALUE_TYPE.strands :
break; var txt = __txt("Strands Object");
if(is_struct(content))
case VALUE_TYPE.buffer : txt += " (strands: " + string(array_length(content.hairs)) + ")";
draw_tooltip_buffer(content); draw_tooltip_text("[" + txt + "]");
break; break;
case "sprite" : case VALUE_TYPE.mesh :
draw_tooltip_sprite(content); var txt = __txt("Mesh Object");
break; if(is_struct(content))
txt += " (triangles: " + string(array_length(content.triangles)) + ")";
default : draw_tooltip_text("[" + txt + "]");
var tt = ""; break;
if(is_struct(content)) tt = $"[{instanceof(content)}] {content}";
else tt = string(content); case VALUE_TYPE.d3vertex :
var txt = __txt("3D Vertex");
draw_tooltip_text(tt); txt += " (groups: " + string(array_length(content)) + ")";
} draw_tooltip_text("[" + txt + "]");
} else if(TOOLTIP != "") break;
draw_tooltip_text(TOOLTIP);
case VALUE_TYPE.buffer :
draw_tooltip_buffer(content);
break;
case "sprite" :
draw_tooltip_sprite(content);
break;
default :
var tt = "";
if(is_struct(content)) tt = $"[{instanceof(content)}] {content}";
else tt = string(content);
draw_tooltip_text(tt);
}
} else if(TOOLTIP != "")
draw_tooltip_text(TOOLTIP);
}
TOOLTIP = ""; TOOLTIP = "";
#endregion #endregion

View file

@ -0,0 +1,670 @@
// 2024-05-01 17:17:25
// feather ignore all
/// @func BBMOD_Quaternion([_x, _y, _z, _w])
///
/// @desc A quaternion.
///
/// @param {Real} [_x] The first component of the quaternion. Defaults to 0.
/// @param {Real} [_y] The second component of the quaternion. Defaults to 0.
/// @param {Real} [_z] The third component of the quaternion. Defaults to 0.
/// @param {Real} [_w] The fourth component of the quaternion. Defaults to 1.
///
/// @note If you leave the arguments to their default values, then an identity
/// quaternion is created.
function BBMOD_Quaternion(_x=0.0, _y=0.0, _z=0.0, _w=1.0) constructor
{
/// @var {Real} The first component of the quaternion.
X = _x;
/// @var {Real} The second component of the quaternion.
Y = _y;
/// @var {Real} The third component of the quaternion.
Z = _z;
/// @var {Real} The fourth component of the quaternion.
W = _w;
static set = function(x, y, z, w) {
INLINE
X = x;
Y = y;
Z = z;
W = w;
return self;
}
/// @func Add(_q)
///
/// @desc Adds quaternions and returns the result as a new quaternion.
///
/// @param {Struct.BBMOD_Quaternion} _q The other quaternion.
///
/// @return {Struct.BBMOD_Quaternion} The created quaternion.
static Add = function (_q) {
INLINE
return new BBMOD_Quaternion(
X + _q.X,
Y + _q.Y,
Z + _q.Z,
W + _q.W
);
};
/// @func Clone()
///
/// @desc Creates a clone of the quaternion.
///
/// @return {Struct.BBMOD_Quaternion} The created quaternion.
static Clone = function () {
INLINE
return new BBMOD_Quaternion(X, Y, Z, W);
};
/// @func Conjugate()
///
/// @desc Conjugates the quaternion and returns the result as a quaternion.
///
/// @return {Struct.BBMOD_Quaternion} The created quaternion.
static Conjugate = function () {
INLINE
return new BBMOD_Quaternion(-X, -Y, -Z, W);
};
/// @func Copy(_dest)
///
/// @desc Copies components of the quaternion into other quaternion.
///
/// @param {Struct.BBMOD_Quaternion} _dest The destination quaternion.
///
/// @return {Struct.BBMOD_Quaternion} Returns `self`.
static Copy = function (_dest) {
INLINE
_dest.X = X;
_dest.Y = Y;
_dest.Z = Z;
_dest.W = W;
return self;
};
/// @func Dot(_q)
///
/// @desc Computes a dot product of two dual quaternions.
///
/// @param {Struct.BBMOD_Quaternion} _q The other quaternion.
///
/// @return {Real} The dot product of the quaternions.
static Dot = function (_q) {
INLINE
return (
X * _q.X
+ Y * _q.Y
+ Z * _q.Z
+ W * _q.W
);
};
/// @func Exp()
///
/// @desc Computes an exponential map of the quaternion and returns
/// the result as a new quaternion.
///
/// @return {Struct.BBMOD_Quaternion} The created quaternion.
static Exp = function () {
INLINE
var _length = Length();
if (_length >= math_get_epsilon())
{
var _sinc = Sinc(_length);
return new BBMOD_Quaternion(
X * _sinc,
Y * _sinc,
Z * _sinc,
exp(W) * cos(_length)
);
}
return new BBMOD_Quaternion(0.0, 0.0, 0.0, exp(W));
};
/// @func FromArray(_array[, _index])
///
/// @desc Loads quaternion components `(x, y, z, w)` from an array.
///
/// @param {Array<Real>} _array The array to read the quaternion components
/// from.
/// @param {Real} [_index] The index to start reading the quaternion
/// components from. Defaults to 0.
///
/// @return {Struct.BBMOD_Quaternion} Returns `self`.
static FromArray = function (_array, _index=0) {
INLINE
X = _array[_index];
Y = _array[_index + 1];
Z = _array[_index + 2];
W = _array[_index + 3];
return self;
};
/// @func FromAxisAngle(_axis, _angle)
///
/// @desc Initializes the quaternion using an axis and an angle.
///
/// @param {Struct.BBMOD_Vec3} _axis The axis of rotaiton.
///
/// @param {Real} _angle The rotation angle.
///
/// @return {Struct.BBMOD_Quaternion} Returns `self`.
static FromAxisAngle = function (_axis, _angle) {
INLINE
_angle = -_angle;
var _sinHalfAngle = dsin(_angle * 0.5);
X = is_nan(_axis.X)? 0 : _axis.X * _sinHalfAngle;
Y = is_nan(_axis.Y)? 0 : _axis.Y * _sinHalfAngle;
Z = is_nan(_axis.Z)? 0 : _axis.Z * _sinHalfAngle;
W = dcos(_angle * 0.5);
return self;
};
/// @func FromBuffer(_buffer, _type)
///
/// @desc Loads quaternion components `(x, y, z, w)` from a buffer.
///
/// @param {Id.Buffer} _buffer The buffer to read the quaternion components
/// from.
///
/// @param {Constant.BufferDataType} [_type] The type of each component.
///
/// @return {Struct.BBMOD_Quaternion} Returns `self`.
static FromBuffer = function (_buffer, _type) {
INLINE
X = buffer_read(_buffer, _type);
Y = buffer_read(_buffer, _type);
Z = buffer_read(_buffer, _type);
W = buffer_read(_buffer, _type);
return self;
};
/// @func FromEuler(_x, _y, _z)
///
/// @desc Initializes the quaternion using euler angles.
///
/// @param {Real} _x The rotation around the X axis (in degrees).
/// @param {Real} _y The rotation around the Y axis (in degrees).
/// @param {Real} _z The rotation around the Z axis (in degrees).
///
/// @return {Struct.BBMOD_Quaternion} Returns `self`.
///
/// @note The order of rotations is YXZ, same as in the `matrix_build`
/// function.
static FromEuler = function (_x, _y, _z) {
INLINE
_x = -_x * 0.5;
_y = -_y * 0.5;
_z = -_z * 0.5;
var _q1Sin, _q1Cos, _temp;
var _qX, _qY, _qZ, _qW;
_q1Sin = dsin(_z);
_q1Cos = dcos(_z);
_temp = dsin(_x);
_qX = _q1Cos * _temp;
_qY = _q1Sin * _temp;
_temp = dcos(_x);
_qZ = _q1Sin * _temp;
_qW = _q1Cos * _temp;
_q1Sin = dsin(_y);
_q1Cos = dcos(_y);
X = _qX * _q1Cos - _qZ * _q1Sin;
Y = _qW * _q1Sin + _qY * _q1Cos;
Z = _qZ * _q1Cos + _qX * _q1Sin;
W = _qW * _q1Cos - _qY * _q1Sin;
return self;
};
/// @func FromLookRotation(_forward, _up)
///
/// @desc Initializes the quaternion using a forward and an up vector. These
/// vectors must not be parallel! If they are, the quaternion will be set to an
/// identity.
///
/// @param {Struct.BBMOD_Vec3} _forward The vector facing forward.
/// @param {Struct.BBMOD_Vec3} _up The vector facing up.
///
/// @return {Struct.BBMOD_Quaternion} Returns `self`.
static FromLookRotation = function (_forward, _up) {
INLINE
_forward = new BBMOD_Vec3(_forward);
_up = new BBMOD_Vec3(_up);
if (!_forward.Orthonormalize(_up)) {
X = 0.0;
Y = 0.0;
Z = 0.0;
W = 1.0;
return self;
}
var _right = _up.Cross(_forward);
var _w = sqrt(abs(1.0 + _right.X + _up.Y + _forward.Z)) * 0.5;
var _w4Recip = _w == 0? 0 : 1.0 / (4.0 * _w);
X = (_up.Z - _forward.Y) * _w4Recip;
Y = (_forward.X - _right.Z) * _w4Recip;
Z = (_right.Y - _up.X) * _w4Recip;
W = _w;
return self;
};
static FromMatrix = function(rotMatrix) {
INLINE
W = sqrt(1 + rotMatrix[0] + rotMatrix[5] + rotMatrix[10]) / 2;
X = (rotMatrix[9] - rotMatrix[6]) / (4 * W);
Y = (rotMatrix[2] - rotMatrix[8]) / (4 * W);
Z = (rotMatrix[4] - rotMatrix[1]) / (4 * W);
return self;
}
/// @func GetAngle()
///
/// @desc Retrieves the rotation angle of the quaternion.
///
/// @return {Real} The rotation angle.
static GetAngle = function () {
INLINE
return radtodeg(arccos(W) * 2.0);
};
/// @func GetAxis()
///
/// @desc Retrieves the axis of rotation of the quaternion.
///
/// @return {Struct.BBMOD_Vec3} The axis of rotation.
static GetAxis = function () {
INLINE
var _sinThetaInv = 1.0 / sin(arccos(W));
return new BBMOD_Vec3(
X * _sinThetaInv,
Y * _sinThetaInv,
Z * _sinThetaInv
);
};
/// @func Inverse()
///
/// @desc Computes an inverse of the quaternion and returns the result
/// as a new quaternion.
///
/// @return {Struct.BBMOD_Quaternion} The created quaternion.
static Inverse = function () {
INLINE
return Length() == 0? new BBMOD_Quaternion() : Conjugate().Scale(1.0 / Length());
};
/// @func Length()
///
/// @desc Computes the length of the quaternion.
///
/// @return {Real} The length of the quaternion.
static Length = function () {
INLINE
return sqrt(
X * X
+ Y * Y
+ Z * Z
+ W * W
);
};
/// @func LengthSqr()
///
/// @desc Computes a squared length of the quaternion.
///
/// @return {Real} The squared length of the quaternion.
static LengthSqr = function () {
INLINE
return (
X * X
+ Y * Y
+ Z * Z
+ W * W
);
};
/// @func Lerp(_q, _s)
///
/// @desc Computes a linear interpolation of two quaternions
/// and returns the result as a new quaternion.
///
/// @param {Struct.BBMOD_Quaternion} _q The other quaternion.
/// @param {Real} _s The interpolation factor.
///
/// @return {Struct.BBMOD_Quaternion} The created quaternion.
static Lerp = function (_q, _s) {
INLINE
return new BBMOD_Quaternion(
lerp(X, _q.X, _s),
lerp(Y, _q.Y, _s),
lerp(Z, _q.Z, _s),
lerp(W, _q.W, _s)
);
};
/// @func Log()
///
/// @desc Computes the logarithm map of the quaternion and returns the
/// result as a new quaternion.
///
/// @return {Struct.BBMOD_Quaternion} The created quaternion.
static Log = function () {
INLINE
var _length = Length();
var _w = logn(2.71828, _length);
var _a = arccos(W / _length);
if (_a >= math_get_epsilon())
{
var _mag = 1.0 / _length / Sinc(_a);
return new BBMOD_Quaternion(
X * _mag,
Y * _mag,
Z * _mag,
_w
);
}
return new BBMOD_Quaternion(0.0, 0.0, 0.0, _w);
};
/// @func Mul(_q)
///
/// @desc Multiplies two quaternions and returns the result as a new
/// quaternion.
///
/// @param {Struct.BBMOD_Quaternion} _q The other quaternion.
///
/// @return {Struct.BBMOD_Quaternion} The created quaternion.
static Mul = function (_q) {
INLINE
return new BBMOD_Quaternion(
W * _q.X + X * _q.W + Y * _q.Z - Z * _q.Y,
W * _q.Y + Y * _q.W + Z * _q.X - X * _q.Z,
W * _q.Z + Z * _q.W + X * _q.Y - Y * _q.X,
W * _q.W - X * _q.X - Y * _q.Y - Z * _q.Z
);
};
/// @func Normalize()
///
/// @desc Normalizes the quaternion and returns the result as a new
/// quaternion.
///
/// @return {Struct.BBMOD_Quaternion} The created quaternion.
static Normalize = function () {
INLINE
var _lengthSqr = LengthSqr();
if(_lengthSqr == 0)
return new BBMOD_Quaternion();
if (_lengthSqr >= math_get_epsilon())
return Scale(1.0 / sqrt(_lengthSqr));
return Clone();
};
/// @func Rotate(_v)
///
/// @desc Rotates a vector using the quaternion and returns the result
/// as a new vector.
///
/// @param {Struct.BBMOD_Vec3} _v The vector to rotate.
///
/// @return {Struct.BBMOD_Vec3} The created vector.
static Rotate = function (_v) {
INLINE
var _tovec = is_instanceof(_v, __vec3);
if(_tovec) _v = new BBMOD_Vec3(_v.x, _v.y, _v.z);
var _q = Normalize();
var _V = new BBMOD_Quaternion(_v.X, _v.Y, _v.Z, 0.0);
var _rot = _q.Mul(_V).Mul(_q.Conjugate());
var res;
if(_tovec) res = new __vec3(_rot.X, _rot.Y, _rot.Z);
else res = new BBMOD_Vec3(_rot.X, _rot.Y, _rot.Z);
return res;
};
/// @func Scale(_s)
///
/// @desc Scales each component of the quaternion by a real value and
/// returns the result as a new quaternion.
///
/// @param {Real} _s The value to scale the quaternion by.
///
/// @return {Struct.BBMOD_Quaternion} The created quaternion.
static Scale = function (_s) {
INLINE
return new BBMOD_Quaternion(
X * _s,
Y * _s,
Z * _s,
W * _s
);
};
static Sinc = function (_x) {
INLINE
return (_x >= math_get_epsilon()) ? (sin(_x) / _x) : 1.0;
};
/// @func Slerp(_q, _s)
///
/// @desc Computes a spherical linear interpolation of two quaternions
/// and returns the result as a new quaternion.
///
/// @param {Struct.BBMOD_Quaternion} _q The other quaternion.
/// @param {Real} _s The interpolation factor.
///
/// @return {Struct.BBMOD_Quaternion} The created quaternion.
static Slerp = function (_q, _s) {
INLINE
var _q10 = X;
var _q11 = Y;
var _q12 = Z;
var _q13 = W;
var _q20 = _q.X;
var _q21 = _q.Y;
var _q22 = _q.Z;
var _q23 = _q.W;
var _norm;
_norm = 1.0 / sqrt(_q10 * _q10
+ _q11 * _q11
+ _q12 * _q12
+ _q13 * _q13);
_q10 *= _norm;
_q11 *= _norm;
_q12 *= _norm;
_q13 *= _norm;
_norm = sqrt(_q20 * _q20
+ _q21 * _q21
+ _q22 * _q22
+ _q23 * _q23);
_q20 *= _norm;
_q21 *= _norm;
_q22 *= _norm;
_q23 *= _norm;
var _dot = _q10 * _q20
+ _q11 * _q21
+ _q12 * _q22
+ _q13 * _q23;
if (_dot < 0.0)
{
_dot = -_dot;
_q20 *= -1.0;
_q21 *= -1.0;
_q22 *= -1.0;
_q23 *= -1.0;
}
if (_dot > 0.9995)
{
return new BBMOD_Quaternion(
lerp(_q10, _q20, _s),
lerp(_q11, _q21, _s),
lerp(_q12, _q22, _s),
lerp(_q13, _q23, _s)
);
}
var _theta0 = arccos(_dot);
var _theta = _theta0 * _s;
var _sinTheta = sin(_theta);
var _sinTheta0 = sin(_theta0);
var _s2 = _sinTheta / _sinTheta0;
var _s1 = cos(_theta) - (_dot * _s2);
return new BBMOD_Quaternion(
(_q10 * _s1) + (_q20 * _s2),
(_q11 * _s1) + (_q21 * _s2),
(_q12 * _s1) + (_q22 * _s2),
(_q13 * _s1) + (_q23 * _s2)
);
};
/// @func ToArray([_array[, _index]])
///
/// @desc Writes components `(x, y, z, w)` of the quaternion into an array.
///
/// @param {Array<Real>} [_array] The destination array. If not defined, a
/// new one is created.
/// @param {Real} [_index] The index to start writing to. Defaults to 0.
///
/// @return {Array<Real>} Returns the destination array.
static ToArray = function (_array=undefined, _index=0) {
INLINE
_array ??= array_create(4, 0.0);
_array[@ _index] = X;
_array[@ _index + 1] = Y;
_array[@ _index + 2] = Z;
_array[@ _index + 3] = W;
return _array;
};
/// @func ToBuffer(_buffer, _type)
///
/// @desc Writes the quaternion into a buffer.
///
/// @param {Id.Buffer} _buffer The buffer to write the quaternion to.
/// @param {Constant.BufferDataType} _type The type of each component.
///
/// @return {Struct.BBMOD_Quaternion} Returns `self`.
static ToBuffer = function (_buffer, _type) {
INLINE
buffer_write(_buffer, _type, X);
buffer_write(_buffer, _type, Y);
buffer_write(_buffer, _type, Z);
buffer_write(_buffer, _type, W);
return self;
};
static ToEuler = function(isArray = false) {
var ysqr = Y * Y;
// roll (x-axis rotation)
var t0 = +2.0 * (W * X + Y * Z);
var t1 = +1.0 - 2.0 * (X * X + ysqr);
var roll = arctan2(t0, t1);
// pitch (y-axis rotation)
var t2 = +2.0 * (W * Y - Z * X);
t2 = clamp(t2, -1.0, 1.0); // Prevent numerical instability
var pitch = arcsin(t2);
// yaw (z-axis rotation)
var t3 = +2.0 * (W * Z + X * Y);
var t4 = +1.0 - 2.0 * (ysqr + Z * Z);
var yaw = arctan2(t3, t4);
// Convert radians to degrees
var _dx = roll * 180.0 / pi;
var _dy = pitch * 180.0 / pi;
var _dz = yaw * 180.0 / pi;
_dx = round(_dx * 1000) / 1000;
_dy = round(_dy * 1000) / 1000;
_dz = round(_dz * 1000) / 1000;
return isArray? [ _dx, _dy, _dz ] : new __rot3(_dx, _dy, _dz);
}
/// @func ToMatrix([_dest[, _index]])
///
/// @desc Converts quaternion into a matrix.
///
/// @param {Array<Real>} [_dest] The destination array. If not specified, a
/// new one is created.
/// @param {Real} [_index] The starting index in the destination array.
/// Defaults to 0.
///
/// @return {Array<Real>} Returns the destination array.
static ToMatrix = function (_dest=undefined, _index=0) {
INLINE
_dest ??= matrix_build_identity();
if(is_nan(X)) return _dest;
var _norm = Normalize();
var _temp0, _temp1, _temp2;
var _q0 = _norm.X;
var _q1 = _norm.Y;
var _q2 = _norm.Z;
var _q3 = _norm.W;
_temp0 = _q0 * _q0;
_temp1 = _q1 * _q1;
_temp2 = _q2 * _q2;
_dest[@ _index] = 1.0 - 2.0 * (_temp1 + _temp2);
_dest[@ _index + 5] = 1.0 - 2.0 * (_temp0 + _temp2);
_dest[@ _index + 10] = 1.0 - 2.0 * (_temp0 + _temp1);
_temp0 = _q0 * _q1;
_temp1 = _q3 * _q2;
_dest[@ _index + 1] = 2.0 * (_temp0 + _temp1);
_dest[@ _index + 4] = 2.0 * (_temp0 - _temp1);
_temp0 = _q0 * _q2
_temp1 = _q3 * _q1;
_dest[@ _index + 2] = 2.0 * (_temp0 - _temp1);
_dest[@ _index + 8] = 2.0 * (_temp0 + _temp1);
_temp0 = _q1 * _q2;
_temp1 = _q3 * _q0;
_dest[@ _index + 6] = 2.0 * (_temp0 + _temp1);
_dest[@ _index + 9] = 2.0 * (_temp0 - _temp1);
return _dest;
};
}

View file

@ -0,0 +1,670 @@
// 2024-05-01 17:17:23
// feather ignore all
/// @func BBMOD_Quaternion([_x, _y, _z, _w])
///
/// @desc A quaternion.
///
/// @param {Real} [_x] The first component of the quaternion. Defaults to 0.
/// @param {Real} [_y] The second component of the quaternion. Defaults to 0.
/// @param {Real} [_z] The third component of the quaternion. Defaults to 0.
/// @param {Real} [_w] The fourth component of the quaternion. Defaults to 1.
///
/// @note If you leave the arguments to their default values, then an identity
/// quaternion is created.
function BBMOD_Quaternion(_x=0.0, _y=0.0, _z=0.0, _w=1.0) constructor
{
/// @var {Real} The first component of the quaternion.
X = _x;
/// @var {Real} The second component of the quaternion.
Y = _y;
/// @var {Real} The third component of the quaternion.
Z = _z;
/// @var {Real} The fourth component of the quaternion.
W = _w;
static set = function(x, y, z, w) {
INLINE
X = x;
Y = y;
Z = z;
W = w;
return self;
}
/// @func Add(_q)
///
/// @desc Adds quaternions and returns the result as a new quaternion.
///
/// @param {Struct.BBMOD_Quaternion} _q The other quaternion.
///
/// @return {Struct.BBMOD_Quaternion} The created quaternion.
static Add = function (_q) {
INLINE
return new BBMOD_Quaternion(
X + _q.X,
Y + _q.Y,
Z + _q.Z,
W + _q.W
);
};
/// @func Clone()
///
/// @desc Creates a clone of the quaternion.
///
/// @return {Struct.BBMOD_Quaternion} The created quaternion.
static Clone = function () {
INLINE
return new BBMOD_Quaternion(X, Y, Z, W);
};
/// @func Conjugate()
///
/// @desc Conjugates the quaternion and returns the result as a quaternion.
///
/// @return {Struct.BBMOD_Quaternion} The created quaternion.
static Conjugate = function () {
INLINE
return new BBMOD_Quaternion(-X, -Y, -Z, W);
};
/// @func Copy(_dest)
///
/// @desc Copies components of the quaternion into other quaternion.
///
/// @param {Struct.BBMOD_Quaternion} _dest The destination quaternion.
///
/// @return {Struct.BBMOD_Quaternion} Returns `self`.
static Copy = function (_dest) {
INLINE
_dest.X = X;
_dest.Y = Y;
_dest.Z = Z;
_dest.W = W;
return self;
};
/// @func Dot(_q)
///
/// @desc Computes a dot product of two dual quaternions.
///
/// @param {Struct.BBMOD_Quaternion} _q The other quaternion.
///
/// @return {Real} The dot product of the quaternions.
static Dot = function (_q) {
INLINE
return (
X * _q.X
+ Y * _q.Y
+ Z * _q.Z
+ W * _q.W
);
};
/// @func Exp()
///
/// @desc Computes an exponential map of the quaternion and returns
/// the result as a new quaternion.
///
/// @return {Struct.BBMOD_Quaternion} The created quaternion.
static Exp = function () {
INLINE
var _length = Length();
if (_length >= math_get_epsilon())
{
var _sinc = Sinc(_length);
return new BBMOD_Quaternion(
X * _sinc,
Y * _sinc,
Z * _sinc,
exp(W) * cos(_length)
);
}
return new BBMOD_Quaternion(0.0, 0.0, 0.0, exp(W));
};
/// @func FromArray(_array[, _index])
///
/// @desc Loads quaternion components `(x, y, z, w)` from an array.
///
/// @param {Array<Real>} _array The array to read the quaternion components
/// from.
/// @param {Real} [_index] The index to start reading the quaternion
/// components from. Defaults to 0.
///
/// @return {Struct.BBMOD_Quaternion} Returns `self`.
static FromArray = function (_array, _index=0) {
INLINE
X = _array[_index];
Y = _array[_index + 1];
Z = _array[_index + 2];
W = _array[_index + 3];
return self;
};
/// @func FromAxisAngle(_axis, _angle)
///
/// @desc Initializes the quaternion using an axis and an angle.
///
/// @param {Struct.BBMOD_Vec3} _axis The axis of rotaiton.
///
/// @param {Real} _angle The rotation angle.
///
/// @return {Struct.BBMOD_Quaternion} Returns `self`.
static FromAxisAngle = function (_axis, _angle) {
INLINE
_angle = -_angle;
var _sinHalfAngle = dsin(_angle * 0.5);
X = is_nan(_axis.X)? 0 : _axis.X * _sinHalfAngle;
Y = is_nan(_axis.Y)? 0 : _axis.Y * _sinHalfAngle;
Z = is_nan(_axis.Z)? 0 : _axis.Z * _sinHalfAngle;
W = dcos(_angle * 0.5);
return self;
};
/// @func FromBuffer(_buffer, _type)
///
/// @desc Loads quaternion components `(x, y, z, w)` from a buffer.
///
/// @param {Id.Buffer} _buffer The buffer to read the quaternion components
/// from.
///
/// @param {Constant.BufferDataType} [_type] The type of each component.
///
/// @return {Struct.BBMOD_Quaternion} Returns `self`.
static FromBuffer = function (_buffer, _type) {
INLINE
X = buffer_read(_buffer, _type);
Y = buffer_read(_buffer, _type);
Z = buffer_read(_buffer, _type);
W = buffer_read(_buffer, _type);
return self;
};
/// @func FromEuler(_x, _y, _z)
///
/// @desc Initializes the quaternion using euler angles.
///
/// @param {Real} _x The rotation around the X axis (in degrees).
/// @param {Real} _y The rotation around the Y axis (in degrees).
/// @param {Real} _z The rotation around the Z axis (in degrees).
///
/// @return {Struct.BBMOD_Quaternion} Returns `self`.
///
/// @note The order of rotations is YXZ, same as in the `matrix_build`
/// function.
static FromEuler = function (_x, _y, _z) {
INLINE
_x = -_x * 0.5;
_y = -_y * 0.5;
_z = -_z * 0.5;
var _q1Sin, _q1Cos, _temp;
var _qX, _qY, _qZ, _qW;
_q1Sin = dsin(_z);
_q1Cos = dcos(_z);
_temp = dsin(_x);
_qX = _q1Cos * _temp;
_qY = _q1Sin * _temp;
_temp = dcos(_x);
_qZ = _q1Sin * _temp;
_qW = _q1Cos * _temp;
_q1Sin = dsin(_y);
_q1Cos = dcos(_y);
X = _qX * _q1Cos - _qZ * _q1Sin;
Y = _qW * _q1Sin + _qY * _q1Cos;
Z = _qZ * _q1Cos + _qX * _q1Sin;
W = _qW * _q1Cos - _qY * _q1Sin;
return self;
};
/// @func FromLookRotation(_forward, _up)
///
/// @desc Initializes the quaternion using a forward and an up vector. These
/// vectors must not be parallel! If they are, the quaternion will be set to an
/// identity.
///
/// @param {Struct.BBMOD_Vec3} _forward The vector facing forward.
/// @param {Struct.BBMOD_Vec3} _up The vector facing up.
///
/// @return {Struct.BBMOD_Quaternion} Returns `self`.
static FromLookRotation = function (_forward, _up) {
INLINE
_forward = new BBMOD_Vec3(_forward);
_up = new BBMOD_Vec3(_up);
if (!_forward.Orthonormalize(_up)) {
X = 0.0;
Y = 0.0;
Z = 0.0;
W = 1.0;
return self;
}
var _right = _up.Cross(_forward);
var _w = sqrt(abs(1.0 + _right.X + _up.Y + _forward.Z)) * 0.5;
var _w4Recip = _w == 0? 0 : 1.0 / (4.0 * _w);
X = (_up.Z - _forward.Y) * _w4Recip;
Y = (_forward.X - _right.Z) * _w4Recip;
Z = (_right.Y - _up.X) * _w4Recip;
W = _w;
return self;
};
static FromMatrix = function(rotMatrix) {
INLINE
W = sqrt(1 + rotMatrix[0] + rotMatrix[5] + rotMatrix[10]) / 2;
X = (rotMatrix[9] - rotMatrix[6]) / (4 * W);
Y = (rotMatrix[2] - rotMatrix[8]) / (4 * W);
Z = (rotMatrix[4] - rotMatrix[1]) / (4 * W);
return self;
}
/// @func GetAngle()
///
/// @desc Retrieves the rotation angle of the quaternion.
///
/// @return {Real} The rotation angle.
static GetAngle = function () {
INLINE
return radtodeg(arccos(W) * 2.0);
};
/// @func GetAxis()
///
/// @desc Retrieves the axis of rotation of the quaternion.
///
/// @return {Struct.BBMOD_Vec3} The axis of rotation.
static GetAxis = function () {
INLINE
var _sinThetaInv = 1.0 / sin(arccos(W));
return new BBMOD_Vec3(
X * _sinThetaInv,
Y * _sinThetaInv,
Z * _sinThetaInv
);
};
/// @func Inverse()
///
/// @desc Computes an inverse of the quaternion and returns the result
/// as a new quaternion.
///
/// @return {Struct.BBMOD_Quaternion} The created quaternion.
static Inverse = function () {
INLINE
return Length() == 0? new BBMOD_Quaternion() : Conjugate().Scale(1.0 / Length());
};
/// @func Length()
///
/// @desc Computes the length of the quaternion.
///
/// @return {Real} The length of the quaternion.
static Length = function () {
INLINE
return sqrt(
X * X
+ Y * Y
+ Z * Z
+ W * W
);
};
/// @func LengthSqr()
///
/// @desc Computes a squared length of the quaternion.
///
/// @return {Real} The squared length of the quaternion.
static LengthSqr = function () {
INLINE
return (
X * X
+ Y * Y
+ Z * Z
+ W * W
);
};
/// @func Lerp(_q, _s)
///
/// @desc Computes a linear interpolation of two quaternions
/// and returns the result as a new quaternion.
///
/// @param {Struct.BBMOD_Quaternion} _q The other quaternion.
/// @param {Real} _s The interpolation factor.
///
/// @return {Struct.BBMOD_Quaternion} The created quaternion.
static Lerp = function (_q, _s) {
INLINE
return new BBMOD_Quaternion(
lerp(X, _q.X, _s),
lerp(Y, _q.Y, _s),
lerp(Z, _q.Z, _s),
lerp(W, _q.W, _s)
);
};
/// @func Log()
///
/// @desc Computes the logarithm map of the quaternion and returns the
/// result as a new quaternion.
///
/// @return {Struct.BBMOD_Quaternion} The created quaternion.
static Log = function () {
INLINE
var _length = Length();
var _w = logn(2.71828, _length);
var _a = arccos(W / _length);
if (_a >= math_get_epsilon())
{
var _mag = 1.0 / _length / Sinc(_a);
return new BBMOD_Quaternion(
X * _mag,
Y * _mag,
Z * _mag,
_w
);
}
return new BBMOD_Quaternion(0.0, 0.0, 0.0, _w);
};
/// @func Mul(_q)
///
/// @desc Multiplies two quaternions and returns the result as a new
/// quaternion.
///
/// @param {Struct.BBMOD_Quaternion} _q The other quaternion.
///
/// @return {Struct.BBMOD_Quaternion} The created quaternion.
static Mul = function (_q) {
INLINE
return new BBMOD_Quaternion(
W * _q.X + X * _q.W + Y * _q.Z - Z * _q.Y,
W * _q.Y + Y * _q.W + Z * _q.X - X * _q.Z,
W * _q.Z + Z * _q.W + X * _q.Y - Y * _q.X,
W * _q.W - X * _q.X - Y * _q.Y - Z * _q.Z
);
};
/// @func Normalize()
///
/// @desc Normalizes the quaternion and returns the result as a new
/// quaternion.
///
/// @return {Struct.BBMOD_Quaternion} The created quaternion.
static Normalize = function () {
INLINE
var _lengthSqr = LengthSqr();
if(_lengthSqr == 0)
return new BBMOD_Quaternion();
if (_lengthSqr >= math_get_epsilon())
return Scale(1.0 / sqrt(_lengthSqr));
return Clone();
};
/// @func Rotate(_v)
///
/// @desc Rotates a vector using the quaternion and returns the result
/// as a new vector.
///
/// @param {Struct.BBMOD_Vec3} _v The vector to rotate.
///
/// @return {Struct.BBMOD_Vec3} The created vector.
static Rotate = function (_v) {
INLINE
var _tovec = is_instanceof(_v, __vec3);
if(_tovec) _v = new BBMOD_Vec3(_v.x, _v.y, _v.z);
var _q = Normalize();
var _V = new BBMOD_Quaternion(_v.X, _v.Y, _v.Z, 0.0);
var _rot = _q.Mul(_V).Mul(_q.Conjugate());
var res;
if(_tovec) res = new __vec3(_rot.X, _rot.Y, _rot.Z);
else res = new BBMOD_Vec3(_rot.X, _rot.Y, _rot.Z);
return res;
};
/// @func Scale(_s)
///
/// @desc Scales each component of the quaternion by a real value and
/// returns the result as a new quaternion.
///
/// @param {Real} _s The value to scale the quaternion by.
///
/// @return {Struct.BBMOD_Quaternion} The created quaternion.
static Scale = function (_s) {
INLINE
return new BBMOD_Quaternion(
X * _s,
Y * _s,
Z * _s,
W * _s
);
};
static Sinc = function (_x) {
INLINE
return (_x >= math_get_epsilon()) ? (sin(_x) / _x) : 1.0;
};
/// @func Slerp(_q, _s)
///
/// @desc Computes a spherical linear interpolation of two quaternions
/// and returns the result as a new quaternion.
///
/// @param {Struct.BBMOD_Quaternion} _q The other quaternion.
/// @param {Real} _s The interpolation factor.
///
/// @return {Struct.BBMOD_Quaternion} The created quaternion.
static Slerp = function (_q, _s) {
INLINE
var _q10 = X;
var _q11 = Y;
var _q12 = Z;
var _q13 = W;
var _q20 = _q.X;
var _q21 = _q.Y;
var _q22 = _q.Z;
var _q23 = _q.W;
var _norm;
_norm = 1.0 / sqrt(_q10 * _q10
+ _q11 * _q11
+ _q12 * _q12
+ _q13 * _q13);
_q10 *= _norm;
_q11 *= _norm;
_q12 *= _norm;
_q13 *= _norm;
_norm = sqrt(_q20 * _q20
+ _q21 * _q21
+ _q22 * _q22
+ _q23 * _q23);
_q20 *= _norm;
_q21 *= _norm;
_q22 *= _norm;
_q23 *= _norm;
var _dot = _q10 * _q20
+ _q11 * _q21
+ _q12 * _q22
+ _q13 * _q23;
if (_dot < 0.0)
{
_dot = -_dot;
_q20 *= -1.0;
_q21 *= -1.0;
_q22 *= -1.0;
_q23 *= -1.0;
}
if (_dot > 0.9995)
{
return new BBMOD_Quaternion(
lerp(_q10, _q20, _s),
lerp(_q11, _q21, _s),
lerp(_q12, _q22, _s),
lerp(_q13, _q23, _s)
);
}
var _theta0 = arccos(_dot);
var _theta = _theta0 * _s;
var _sinTheta = sin(_theta);
var _sinTheta0 = sin(_theta0);
var _s2 = _sinTheta / _sinTheta0;
var _s1 = cos(_theta) - (_dot * _s2);
return new BBMOD_Quaternion(
(_q10 * _s1) + (_q20 * _s2),
(_q11 * _s1) + (_q21 * _s2),
(_q12 * _s1) + (_q22 * _s2),
(_q13 * _s1) + (_q23 * _s2)
);
};
/// @func ToArray([_array[, _index]])
///
/// @desc Writes components `(x, y, z, w)` of the quaternion into an array.
///
/// @param {Array<Real>} [_array] The destination array. If not defined, a
/// new one is created.
/// @param {Real} [_index] The index to start writing to. Defaults to 0.
///
/// @return {Array<Real>} Returns the destination array.
static ToArray = function (_array=undefined, _index=0) {
INLINE
_array ??= array_create(4, 0.0);
_array[@ _index] = X;
_array[@ _index + 1] = Y;
_array[@ _index + 2] = Z;
_array[@ _index + 3] = W;
return _array;
};
/// @func ToBuffer(_buffer, _type)
///
/// @desc Writes the quaternion into a buffer.
///
/// @param {Id.Buffer} _buffer The buffer to write the quaternion to.
/// @param {Constant.BufferDataType} _type The type of each component.
///
/// @return {Struct.BBMOD_Quaternion} Returns `self`.
static ToBuffer = function (_buffer, _type) {
INLINE
buffer_write(_buffer, _type, X);
buffer_write(_buffer, _type, Y);
buffer_write(_buffer, _type, Z);
buffer_write(_buffer, _type, W);
return self;
};
static ToEuler = function(isArray = false) {
var ysqr = Y * Y;
// roll (x-axis rotation)
var t0 = +2.0 * (W * X + Y * Z);
var t1 = +1.0 - 2.0 * (X * X + ysqr);
var roll = arctan2(t0, t1);
// pitch (y-axis rotation)
var t2 = +2.0 * (W * Y - Z * X);
t2 = clamp(t2, -1.0, 1.0); // Prevent numerical instability
var pitch = arcsin(t2);
// yaw (z-axis rotation)
var t3 = +2.0 * (W * Z + X * Y);
var t4 = +1.0 - 2.0 * (ysqr + Z * Z);
var yaw = arctan2(t3, t4);
// Convert radians to degrees
var _dx = roll * 180.0 / pi;
var _dy = pitch * 180.0 / pi;
var _dz = yaw * 180.0 / pi;
_dx = round(_dx * 1000) / 1000;
_dy = round(_dy * 1000) / 1000;
_dz = round(_dz * 1000) / 1000;
return isArray? [ _dx, _dy, _dz ] : new __rot3(_dx, _dy, _dz);
}
/// @func ToMatrix([_dest[, _index]])
///
/// @desc Converts quaternion into a matrix.
///
/// @param {Array<Real>} [_dest] The destination array. If not specified, a
/// new one is created.
/// @param {Real} [_index] The starting index in the destination array.
/// Defaults to 0.
///
/// @return {Array<Real>} Returns the destination array.
static ToMatrix = function (_dest=undefined, _index=0) {
INLINE
_dest ??= matrix_build_identity();
if(is_nan(X)) return _dest;
var _norm = Normalize();
var _temp0, _temp1, _temp2;
var _q0 = _norm.X;
var _q1 = _norm.Y;
var _q2 = _norm.Z;
var _q3 = _norm.W;
_temp0 = _q0 * _q0;
_temp1 = _q1 * _q1;
_temp2 = _q2 * _q2;
_dest[@ _index] = 1.0 - 2.0 * (_temp1 + _temp2);
_dest[@ _index + 5] = 1.0 - 2.0 * (_temp0 + _temp2);
_dest[@ _index + 10] = 1.0 - 2.0 * (_temp0 + _temp1);
_temp0 = _q0 * _q1;
_temp1 = _q3 * _q2;
_dest[@ _index + 1] = 2.0 * (_temp0 + _temp1);
_dest[@ _index + 4] = 2.0 * (_temp0 - _temp1);
_temp0 = _q0 * _q2
_temp1 = _q3 * _q1;
_dest[@ _index + 2] = 2.0 * (_temp0 - _temp1);
_dest[@ _index + 8] = 2.0 * (_temp0 + _temp1);
_temp0 = _q1 * _q2;
_temp1 = _q3 * _q0;
_dest[@ _index + 6] = 2.0 * (_temp0 + _temp1);
_dest[@ _index + 9] = 2.0 * (_temp0 - _temp1);
return _dest;
};
}

View file

@ -0,0 +1,86 @@
// 2024-05-01 16:00:45
function Node_3D(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) constructor {
name = "3D";
is_3D = true;
surface_depth_disable(false);
mesh_prev_surface = surface_create(64, 64);
static drawOverlay3D = function(active, params, _mx, _my, _snx, _sny, _panel) {}
static processData = function(_outSurf, _data, _output_index, _array_index) {}
static onDrawNode = function(xx, yy, _mx, _my, _s, _hover, _focus) {}
static getPreviewObject = function() { #region
if(ds_list_empty(outputs)) return noone;
switch(outputs[| preview_channel].type) {
case VALUE_TYPE.d3Mesh :
case VALUE_TYPE.d3Light :
case VALUE_TYPE.d3Camera :
case VALUE_TYPE.d3Scene : break;
default : return noone;
}
var _obj = outputs[| 0].getValue();
if(is_array(_obj)) _obj = array_safe_get_fast(_obj, preview_index, noone);
return _obj;
} #endregion
static getPreviewObjects = function() { return [ getPreviewObject() ]; }
static getPreviewObjectOutline = function() { return getPreviewObjects() }
static refreshPreview = function() { #region
var _prev_obj = getPreviewObjects();
mesh_prev_surface = surface_verify(mesh_prev_surface, PREFERENCES.node_3d_preview_size, PREFERENCES.node_3d_preview_size);
surface_set_target(mesh_prev_surface);
DRAW_CLEAR
gpu_set_zwriteenable(true);
gpu_set_ztestenable(true);
gpu_set_cullmode(cull_noculling);
D3D_GLOBAL_PREVIEW.camera.applyCamera();
D3D_GLOBAL_PREVIEW.apply();
for( var i = 0, n = array_length(_prev_obj); i < n; i++ ) {
var _prev = _prev_obj[i];
if(!is_struct(_prev) || !struct_has(_prev, "getBBOX")) continue;
var _b = _prev.getBBOX();
var _c = _prev.getCenter();
if(_b == noone || _c == noone) continue;
D3D_GLOBAL_PREVIEW.custom_transform.position.set(_c._multiply(-1));
var _sca = 2 / _b.getScale();
//print($"Submitting object {_prev}\n{_b}, {_c}, {_sca}");
D3D_GLOBAL_PREVIEW.custom_transform.scale.set(_sca);
D3D_GLOBAL_PREVIEW.submitUI(_prev);
}
surface_reset_target();
D3D_GLOBAL_PREVIEW.camera.resetCamera();
} #endregion
static postProcess = function() { refreshPreview(); }
static onDrawNode = function(xx, yy, _mx, _my, _s, _hover = false, _focus = false) { #region
if(!is_surface(mesh_prev_surface)) return;
if(!previewable) return;
var bbox = drawGetBbox(xx, yy, _s);
var aa = 0.5 + 0.5 * renderActive;
if(!isHighlightingInGraph()) aa *= 0.25;
draw_surface_bbox(mesh_prev_surface, bbox,, aa);
onDrawNodeOver(xx, yy, _mx, _my, _s, _hover, _focus);
} #endregion
static onDrawNodeOver = function(xx, yy, _mx, _my, _s, _hover = false, _focus = false) { }
}

View file

@ -0,0 +1,86 @@
// 2024-05-01 16:00:18
function Node_3D(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) constructor {
name = "3D";
is_3D = true;
surface_depth_disable(false);
mesh_prev_surface = surface_create(64, 64);
static drawOverlay3D = function(active, params, _mx, _my, _snx, _sny, _panel) {}
static processData = function(_outSurf, _data, _output_index, _array_index) {}
static onDrawNode = function(xx, yy, _mx, _my, _s, _hover, _focus) {}
static getPreviewObject = function() { #region
if(ds_list_empty(outputs)) return noone;
switch(outputs[| preview_channel].type) {
case VALUE_TYPE.d3Mesh :
case VALUE_TYPE.d3Light :
case VALUE_TYPE.d3Camera :
case VALUE_TYPE.d3Scene : break;
default : return noone;
}
var _obj = outputs[| 0].getValue();
if(is_array(_obj)) _obj = array_safe_get_fast(_obj, preview_index, noone);
return _obj;
} #endregion
static getPreviewObjects = function() { return [ getPreviewObject() ]; }
static getPreviewObjectOutline = function() { return getPreviewObjects() }
static refreshPreview = function() { #region
var _prev_obj = getPreviewObjects();
mesh_prev_surface = surface_verify(mesh_prev_surface, PREFERENCES.node_3d_preview_size, PREFERENCES.node_3d_preview_size);
surface_set_target(mesh_prev_surface);
DRAW_CLEAR
gpu_set_zwriteenable(true);
gpu_set_ztestenable(true);
gpu_set_cullmode(cull_noculling);
D3D_GLOBAL_PREVIEW.camera.applyCamera();
D3D_GLOBAL_PREVIEW.apply();
for( var i = 0, n = array_length(_prev_obj); i < n; i++ ) {
var _prev = _prev_obj[i];
if(!is_struct(_prev) || !struct_has(_prev, "getBBOX")) continue;
var _b = _prev.getBBOX();
var _c = _prev.getCenter();
if(_b == noone || _c == noone) continue;
D3D_GLOBAL_PREVIEW.custom_transform.position.set(_c._multiply(-1));
var _sca = 2 / _b.getScale();
//print($"Submitting object {_prev}\n{_b}, {_c}, {_sca}");
D3D_GLOBAL_PREVIEW.custom_transform.scale.set(_sca);
D3D_GLOBAL_PREVIEW.submitUI(_prev);
}
surface_reset_target();
D3D_GLOBAL_PREVIEW.camera.resetCamera();
} #endregion
static postProcess = function() { refreshPreview(); }
static onDrawNode = function(xx, yy, _mx, _my, _s, _hover = false, _focus = false) { #region
if(!is_surface(mesh_prev_surface)) return;
if(!previewable) return;
var bbox = drawGetBbox(xx, yy, _s);
var aa = 0.5 + 0.5 * renderActive;
if(!isHighlightingInGraph()) aa *= 0.25;
draw_surface_bbox(mesh_prev_surface, bbox,, aa);
onDrawNodeOver(xx, yy, _mx, _my, _s, _hover, _focus);
} #endregion
static onDrawNodeOver = function(xx, yy, _mx, _my, _s, _hover = false, _focus = false) { }
}

View file

@ -0,0 +1,633 @@
// 2024-05-01 16:28:40
function Node_3D_Object(_x, _y, _group = noone) : Node_3D(_x, _y, _group) constructor {
name = "3D Object";
cached_object = [];
object_class = noone;
preview_channel = 0;
inputs[| 0] = nodeValue("Position", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, [ 0, 0, 0 ])
.setDisplay(VALUE_DISPLAY.vector);
inputs[| 1] = nodeValue("Rotation", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, [ 0, 0, 0, 1 ])
.setDisplay(VALUE_DISPLAY.d3quarternion);
inputs[| 2] = nodeValue("Scale", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, [ 1, 1, 1 ])
.setDisplay(VALUE_DISPLAY.vector);
inputs[| 3] = nodeValue("Anchor", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, [ 0, 0, 0 ])
.setDisplay(VALUE_DISPLAY.vector);
in_d3d = ds_list_size(inputs);
#macro __d3d_input_list_transform ["Transform", false], 0, 3, 1, 2
#region ---- overlay ----
drag_axis = noone;
drag_sv = 0;
drag_delta = 0;
drag_prev = 0;
drag_dist = 0;
drag_val = 0;
drag_mx = 0;
drag_my = 0;
drag_px = 0;
drag_py = 0;
drag_cx = 0;
drag_cy = 0;
drag_rot_axis = new BBMOD_Quaternion();
drag_original = 0;
axis_hover = noone;
#endregion
#region ---- tools ----
tool_pos = new NodeTool( "Transform", THEME.tools_3d_transform, "Node_3D_Object" );
tool_rot = new NodeTool( "Rotate", THEME.tools_3d_rotate, "Node_3D_Object" );
tool_sca = new NodeTool( "Scale", THEME.tools_3d_scale, "Node_3D_Object" );
tools = [ tool_pos, tool_rot, tool_sca ];
tool_axis_edit = new scrollBox([ "local", "global" ], function(val) { tool_attribute.context = val; });
// tool_axis_edit.font = f_p2;
// tool_axis_edit.arrow_spr = THEME.arrow;
// tool_axis_edit.arrow_ind = 3;
tool_attribute.context = 0;
tool_settings = [
[ "Axis", tool_axis_edit, "context", tool_attribute ],
];
static getToolSettings = function() {
if(isUsingTool("Transform") || isUsingTool("Rotate"))
return tool_settings;
return [];
}
#endregion
static drawGizmoPosition = function(index, object, _vpos, active, params, _mx, _my, _snx, _sny, _panel) { #region
#region ---- main ----
var _pos = inputs[| index].getValue(,,, true);
var _qrot = object == noone? new BBMOD_Quaternion() : object.transform.rotation;
var _qinv = new BBMOD_Quaternion().FromAxisAngle(new BBMOD_Vec3(1, 0, 0), 90);
var _camera = params.camera;
var _qview = new BBMOD_Quaternion().FromEuler(_camera.focus_angle_y, -_camera.focus_angle_x, 0);
var _axis = tool_attribute.context;
var _hover = noone;
var _hoverDist = 10;
var th;
var _posView = _camera.worldPointToViewPoint(_vpos);
var cx = _posView.x;
var cy = _posView.y;
var ga = [];
var size = 64;
var hs = size / 2;
var sq = 8;
#endregion
#region display
ga[0] = new BBMOD_Vec3(-size, 0, 0);
ga[1] = new BBMOD_Vec3(0, 0, size);
ga[2] = new BBMOD_Vec3(0, -size, 0);
ga[3] = [ new BBMOD_Vec3(-hs + sq, 0, hs - sq),
new BBMOD_Vec3(-hs - sq, 0, hs - sq),
new BBMOD_Vec3(-hs - sq, 0, hs + sq),
new BBMOD_Vec3(-hs + sq, 0, hs + sq), ];
ga[4] = [ new BBMOD_Vec3( 0, -hs + sq, hs - sq),
new BBMOD_Vec3( 0, -hs - sq, hs - sq),
new BBMOD_Vec3( 0, -hs - sq, hs + sq),
new BBMOD_Vec3( 0, -hs + sq, hs + sq), ];
ga[5] = [ new BBMOD_Vec3(-hs + sq, -hs - sq, 0),
new BBMOD_Vec3(-hs - sq, -hs - sq, 0),
new BBMOD_Vec3(-hs - sq, -hs + sq, 0),
new BBMOD_Vec3(-hs + sq, -hs + sq, 0), ];
ga[0] = new BBMOD_Vec3(-size, 0, 0);
ga[1] = new BBMOD_Vec3(0, -size, 0);
ga[2] = new BBMOD_Vec3(0, 0, -size);
ga[3] = [ new BBMOD_Vec3(-hs + sq, -hs - sq, 0),
new BBMOD_Vec3(-hs - sq, -hs - sq, 0),
new BBMOD_Vec3(-hs - sq, -hs + sq, 0),
new BBMOD_Vec3(-hs + sq, -hs + sq, 0), ];
ga[4] = [ new BBMOD_Vec3( 0, -hs + sq, -hs - sq),
new BBMOD_Vec3( 0, -hs - sq, -hs - sq),
new BBMOD_Vec3( 0, -hs - sq, -hs + sq),
new BBMOD_Vec3( 0, -hs + sq, -hs + sq), ];
ga[5] = [ new BBMOD_Vec3(-hs + sq, 0, -hs - sq),
new BBMOD_Vec3(-hs - sq, 0, -hs - sq),
new BBMOD_Vec3(-hs - sq, 0, -hs + sq),
new BBMOD_Vec3(-hs + sq, 0, -hs + sq), ];
for( var i = 0; i < 3; i++ ) {
if(_axis == 0)
ga[i] = _qview.Rotate(_qinv.Rotate(_qrot.Rotate(ga[i])));
else if(_axis == 1)
ga[i] = _qview.Rotate(_qinv.Rotate(ga[i]));
th = 2 + (axis_hover == i || drag_axis == i);
if(drag_axis != noone && drag_axis != i)
continue;
draw_set_color(COLORS.axis[i]);
if(point_distance(cx, cy, cx + ga[i].X, cy + ga[i].Y) < 5)
draw_line_round(cx, cy, cx + ga[i].X, cy + ga[i].Y, th);
else
draw_line_round_arrow(cx, cy, cx + ga[i].X, cy + ga[i].Y, th, 3);
var _d = distance_to_line(_mx, _my, cx, cy, cx + ga[i].X, cy + ga[i].Y);
if(_d < _hoverDist) {
_hover = i;
_hoverDist = _d;
}
}
for( var i = 3; i < 6; i++ ) {
for( var j = 0; j < 4; j++ ) {
if(_axis == 0)
ga[i][j] = _qview.Rotate(_qinv.Rotate(_qrot.Rotate(ga[i][j])));
else if(_axis == 1)
ga[i][j] = _qview.Rotate(_qinv.Rotate(ga[i][j]));
}
th = 1;
var p0x = cx + ga[i][0].X, p0y = cy + ga[i][0].Y;
var p1x = cx + ga[i][1].X, p1y = cy + ga[i][1].Y;
var p2x = cx + ga[i][2].X, p2y = cy + ga[i][2].Y;
var p3x = cx + ga[i][3].X, p3y = cy + ga[i][3].Y;
var _pax = (p0x + p1x + p2x + p3x) / 4;
var _pay = (p0y + p1y + p2y + p3y) / 4;
if((abs(p0x - _pax) + abs(p1x - _pax) + abs(p2x - _pax) + abs(p3x - _pax)) / 4 < 1)
continue;
if((abs(p0y - _pay) + abs(p1y - _pay) + abs(p2y - _pay) + abs(p3y - _pay)) / 4 < 1)
continue;
draw_set_color(COLORS.axis[(i - 3 - 1 + 3) % 3]);
if(axis_hover == i || drag_axis == i) {
draw_primitive_begin(pr_trianglestrip);
draw_vertex(p0x, p0y);
draw_vertex(p1x, p1y);
draw_vertex(p3x, p3y);
draw_vertex(p2x, p2y);
draw_primitive_end();
} else if (drag_axis == noone) {
draw_line(p0x, p0y, p1x, p1y);
draw_line(p1x, p1y, p2x, p2y);
draw_line(p2x, p2y, p3x, p3y);
draw_line(p3x, p3y, p0x, p0y);
} else
continue;
if(point_in_rectangle_points(_mx, _my, p0x, p0y, p1x, p1y, p3x, p3y, p2x, p2y))
_hover = i;
}
axis_hover = _hover;
#endregion display
if(drag_axis != noone) { #region editing
if(!MOUSE_WRAPPING) {
drag_mx += _mx - drag_px;
drag_my += _my - drag_py;
var mAdj, nor, prj;
var ray = _camera.viewPointToWorldRay(drag_mx, drag_my);
var val = [ drag_val[0], drag_val[1], drag_val[2] ];
if(drag_axis < 3) {
switch(drag_axis) {
case 0 : nor = new __vec3(0, 1, 0); prj = new __vec3(1, 0, 0); break;
case 1 : nor = new __vec3(0, 0, 1); prj = new __vec3(0, 1, 0); break;
case 2 : nor = new __vec3(1, 0, 0); prj = new __vec3(0, 0, 1); break;
}
if(_axis == 0) {
nor = _qrot.Rotate(nor);
prj = _qrot.Rotate(prj);
}
var pln = new __plane(drag_original, nor);
mAdj = d3d_intersect_ray_plane(ray, pln);
if(drag_prev != undefined) {
var _diff = mAdj.subtract(drag_prev);
var _dist = _diff.dot(prj);
for( var i = 0; i < 3; i++ )
val[i] += prj.getIndex(i) * _dist;
if(inputs[| index].setValue(value_snap(val, _snx)))
UNDO_HOLDING = true;
}
} else {
switch(drag_axis) {
case 3 : nor = new __vec3(0, 0, 1); break;
case 4 : nor = new __vec3(1, 0, 0); break;
case 5 : nor = new __vec3(0, 1, 0); break;
}
if(_axis == 0)
nor = _qrot.Rotate(nor);
var pln = new __plane(drag_original, nor);
mAdj = d3d_intersect_ray_plane(ray, pln);
if(drag_prev != undefined) {
var _diff = mAdj.subtract(drag_prev);
for( var i = 0; i < 3; i++ )
val[i] += _diff.getIndex(i);
if(inputs[| index].setValue(value_snap(val, _snx)))
UNDO_HOLDING = true;
}
}
drag_val = [ val[0], val[1], val[2] ];
drag_prev = mAdj;
}
setMouseWrap();
drag_px = _mx;
drag_py = _my;
} #endregion
if(_hover != noone && mouse_press(mb_left, active)) { #region
drag_axis = _hover;
drag_prev = undefined;
drag_mx = _mx;
drag_my = _my;
drag_px = _mx;
drag_py = _my;
drag_cx = cx;
drag_cy = cy;
drag_val = _pos;
drag_original = new __vec3(_pos);
} #endregion
} #endregion
static drawGizmoRotation = function(index, object, _vpos, active, params, _mx, _my, _snx, _sny, _panel) { #region
#region ---- main ----
var _rot = inputs[| index].getValue();
var _qrot = object == noone? new BBMOD_Quaternion() : object.transform.rotation;
var _qinv = new BBMOD_Quaternion().FromAxisAngle(new BBMOD_Vec3(1, 0, 0), 90);
var _camera = params.camera;
var _qview = new BBMOD_Quaternion().FromEuler(_camera.focus_angle_y, -_camera.focus_angle_x, 0);
var _axis = tool_attribute.context;
var _hover = noone;
var _hoverDist = 10;
var th;
var _posView = _camera.worldPointToViewPoint(_vpos);
var cx = _posView.x;
var cy = _posView.y;
var ga = [];
var size = 64;
var hs = size / 2;
var sq = 8;
#endregion
#region display
var size = 64;
for( var i = 0; i < 3; i++ ) {
var op, np;
th = 2 + (axis_hover == i || drag_axis == i);
if(drag_axis != noone && drag_axis != i)
continue;
draw_set_color(COLORS.axis[i]);
for( var j = 0; j <= 32; j++ ) {
var ang = j / 32 * 360;
switch(i) {
case 0 : np = new BBMOD_Vec3(0, lengthdir_x(size, ang), lengthdir_y(size, ang)); break;
case 1 : np = new BBMOD_Vec3(lengthdir_x(size, ang), lengthdir_y(size, ang), 0); break;
case 2 : np = new BBMOD_Vec3(lengthdir_x(size, ang), 0, lengthdir_y(size, ang)); break;
}
if(_axis == 0)
np = _qview.Rotate(_qinv.Rotate(_qrot.Rotate(np)));
else if(_axis == 1)
np = _qview.Rotate(_qinv.Rotate(np));
if(j && (op.Z > 0 && np.Z > 0 || drag_axis == i)) {
draw_line_round(cx + op.X, cy + op.Y, cx + np.X, cy + np.Y, th);
var _d = distance_to_line(_mx, _my, cx + op.X, cy + op.Y, cx + np.X, cy + np.Y);
if(_d < _hoverDist) {
_hover = i;
_hoverDist = _d;
}
}
op = np;
}
}
axis_hover = _hover;
#endregion
if(drag_axis != noone) { #region
var mAng = point_direction(cx, cy, _mx, _my);
if(drag_rot_axis == undefined) {
drag_rot_axis = BBMOD_VEC3_FORWARD;
switch(drag_axis) {
case 0 : drag_rot_axis = new BBMOD_Vec3(-1, 0, 0); break;
case 1 : drag_rot_axis = new BBMOD_Vec3( 0, 0, -1); break;
case 2 : drag_rot_axis = new BBMOD_Vec3( 0, -1, 0); break;
}
if(_axis == 0) drag_rot_axis = _qrot.Rotate(drag_rot_axis).Normalize();
}
var _nv = _qview.Rotate(_qinv.Rotate(drag_rot_axis));
draw_line_round(cx, cy, cx + _nv.X * 100, cy + _nv.Y * 100, 2);
if(drag_prev != undefined) {
var _rd = (mAng - drag_prev) * (_nv.Z > 0? 1 : -1);
drag_dist += _rd;
var _dist = value_snap(drag_dist, _sny);
var _currR = new BBMOD_Quaternion().FromAxisAngle(drag_rot_axis, _dist);
var _val = _currR.Mul(drag_val);
var _Nrot = _val.ToArray();
if(inputs[| index].setValue(_Nrot))
UNDO_HOLDING = true;
}
draw_set_color(COLORS._main_accent);
draw_line_dashed(cx, cy, _mx, _my, 1, 4);
drag_prev = mAng;
} #endregion
if(_hover != noone && mouse_press(mb_left, active)) { #region
drag_axis = _hover;
drag_prev = undefined;
drag_val = _qrot.Clone();
drag_dist = 0;
drag_rot_axis = undefined;
} #endregion
} #endregion
static drawGizmoScale = function(index, object, _vpos, active, params, _mx, _my, _snx, _sny, _panel) { #region
tool_attribute.context = 0;
#region ---- main ----
var _sca = inputs[| index].getValue(,,, true);
var _qrot = object == noone? new BBMOD_Quaternion() : object.transform.rotation;
var _qinv = new BBMOD_Quaternion().FromAxisAngle(new BBMOD_Vec3(1, 0, 0), 90);
var _camera = params.camera;
var _qview = new BBMOD_Quaternion().FromEuler(_camera.focus_angle_y, -_camera.focus_angle_x, 0);
var _axis = tool_attribute.context;
var _hover = noone;
var _hoverDist = 10;
var th;
var _posView = _camera.worldPointToViewPoint(_vpos);
var cx = _posView.x;
var cy = _posView.y;
var ga = [];
var size = 64;
var hs = size / 2;
var sq = 8;
#endregion
#region display
var ga = [];
var size = 64;
var hs = size / 2;
var sq = 8;
ga[0] = new BBMOD_Vec3(-size, 0, 0);
ga[1] = new BBMOD_Vec3(0, -size, 0);
ga[2] = new BBMOD_Vec3(0, 0, -size);
ga[3] = [ new BBMOD_Vec3(-hs + sq, -hs - sq, 0),
new BBMOD_Vec3(-hs - sq, -hs - sq, 0),
new BBMOD_Vec3(-hs - sq, -hs + sq, 0),
new BBMOD_Vec3(-hs + sq, -hs + sq, 0), ];
ga[4] = [ new BBMOD_Vec3( 0, -hs + sq, -hs - sq),
new BBMOD_Vec3( 0, -hs - sq, -hs - sq),
new BBMOD_Vec3( 0, -hs - sq, -hs + sq),
new BBMOD_Vec3( 0, -hs + sq, -hs + sq), ];
ga[5] = [ new BBMOD_Vec3(-hs + sq, 0, -hs - sq),
new BBMOD_Vec3(-hs - sq, 0, -hs - sq),
new BBMOD_Vec3(-hs - sq, 0, -hs + sq),
new BBMOD_Vec3(-hs + sq, 0, -hs + sq), ];
for( var i = 0; i < 3; i++ ) {
ga[i] = _qview.Rotate(_qinv.Rotate(_qrot.Rotate(ga[i])));
th = 2 + (axis_hover == i || drag_axis == i);
if(drag_axis != noone && drag_axis != i)
continue;
draw_set_color(COLORS.axis[i]);
if(point_distance(cx, cy, cx + ga[i].X, cy + ga[i].Y) < 5)
draw_line_round(cx, cy, cx + ga[i].X, cy + ga[i].Y, th);
else
draw_line_round_arrow_block(cx, cy, cx + ga[i].X, cy + ga[i].Y, th, 3);
var _d = distance_to_line(_mx, _my, cx, cy, cx + ga[i].X, cy + ga[i].Y);
if(_d < _hoverDist) {
_hover = i;
_hoverDist = _d;
}
}
for( var i = 3; i < 6; i++ ) {
for( var j = 0; j < 4; j++ ) {
if(_axis == 0)
ga[i][j] = _qview.Rotate(_qinv.Rotate(_qrot.Rotate(ga[i][j])));
else
ga[i][j] = _qview.Rotate(_qinv.Rotate(ga[i][j]));
}
th = 1;
var p0x = cx + ga[i][0].X, p0y = cy + ga[i][0].Y;
var p1x = cx + ga[i][1].X, p1y = cy + ga[i][1].Y;
var p2x = cx + ga[i][2].X, p2y = cy + ga[i][2].Y;
var p3x = cx + ga[i][3].X, p3y = cy + ga[i][3].Y;
var _pax = (p0x + p1x + p2x + p3x) / 4;
var _pay = (p0y + p1y + p2y + p3y) / 4;
if((abs(p0x - _pax) + abs(p1x - _pax) + abs(p2x - _pax) + abs(p3x - _pax)) / 4 < 1)
continue;
if((abs(p0y - _pay) + abs(p1y - _pay) + abs(p2y - _pay) + abs(p3y - _pay)) / 4 < 1)
continue;
draw_set_color(COLORS.axis[(i - 3 - 1 + 3) % 3]);
if(axis_hover == i || drag_axis == i) {
draw_primitive_begin(pr_trianglestrip);
draw_vertex(p0x, p0y);
draw_vertex(p1x, p1y);
draw_vertex(p3x, p3y);
draw_vertex(p2x, p2y);
draw_primitive_end();
} else if (drag_axis == noone) {
draw_line(p0x, p0y, p1x, p1y);
draw_line(p1x, p1y, p2x, p2y);
draw_line(p2x, p2y, p3x, p3y);
draw_line(p3x, p3y, p0x, p0y);
} else
continue;
if(point_in_rectangle_points(_mx, _my, p0x, p0y, p1x, p1y, p3x, p3y, p2x, p2y))
_hover = i;
}
axis_hover = _hover;
#endregion
if(drag_axis != noone) { #region editing
if(!MOUSE_WRAPPING) {
drag_mx += _mx - drag_px;
drag_my += _my - drag_py;
var mAdj, nor, prj;
var ray = _camera.viewPointToWorldRay(drag_mx, drag_my);
if(drag_axis < 3) {
switch(drag_axis) {
case 0 : nor = new __vec3(0, 1, 0); prj = new __vec3(1, 0, 0); break;
case 1 : nor = new __vec3(0, 0, 1); prj = new __vec3(0, 1, 0); break;
case 2 : nor = new __vec3(1, 0, 0); prj = new __vec3(0, 0, 1); break;
}
nor = _qrot.Rotate(nor);
var pln = new __plane(drag_original, nor);
mAdj = d3d_intersect_ray_plane(ray, pln);
if(drag_prev != undefined) {
var _diff = mAdj.subtract(drag_prev);
var _dist = _diff.dot(prj);
drag_val[drag_axis] += prj.getIndex(drag_axis) * _dist;
if(inputs[| index].setValue(value_snap(drag_val, _snx)))
UNDO_HOLDING = true;
}
} else {
switch(drag_axis) {
case 3 : nor = new __vec3(0, 0, 1); break;
case 4 : nor = new __vec3(1, 0, 0); break;
case 5 : nor = new __vec3(0, 1, 0); break;
}
nor = _qrot.Rotate(nor);
var pln = new __plane(drag_original, nor);
mAdj = d3d_intersect_ray_plane(ray, pln);
if(drag_prev != undefined) {
var _diff = mAdj.subtract(drag_prev);
for( var i = 0; i < 3; i++ )
drag_val[i] += _diff.getIndex(i);
if(inputs[| index].setValue(value_snap(drag_val, _snx)))
UNDO_HOLDING = true;
}
}
drag_prev = mAdj;
}
setMouseWrap();
drag_px = _mx;
drag_py = _my;
} #endregion
if(_hover != noone && mouse_press(mb_left, active)) { #region
drag_axis = _hover;
drag_prev = undefined;
drag_mx = _mx;
drag_my = _my;
drag_px = _mx;
drag_py = _my;
drag_cx = cx;
drag_cy = cy;
drag_val = [ _sca[0], _sca[1], _sca[2] ];
drag_original = new __vec3(_sca);
} #endregion
} #endregion
static drawOverlay3D = function(active, params, _mx, _my, _snx, _sny, _panel) { #region
var object = getPreviewObjects();
if(array_empty(object)) return;
object = object[0];
var _pos = inputs[| 0].getValue(,,, true);
var _vpos = new __vec3( _pos[0], _pos[1], _pos[2] );
if(isUsingTool("Transform")) drawGizmoPosition(0, object, _vpos, active, params, _mx, _my, _snx, _sny, _panel);
else if(isUsingTool("Rotate")) drawGizmoRotation(1, object, _vpos, active, params, _mx, _my, _snx, _sny, _panel);
else if(isUsingTool("Scale")) drawGizmoScale(2, object, _vpos, active, params, _mx, _my, _snx, _sny, _panel);
if(drag_axis != noone && mouse_release(mb_left)) {
drag_axis = noone;
UNDO_HOLDING = false;
}
if(onDrawOverlay3D != 0) onDrawOverlay3D(active, params, _mx, _my, _snx, _sny, _panel);
} #endregion
static onDrawOverlay3D = 0;
static setTransform = function(object, _data) { #region
if(object == noone) return;
var _pos = _data[0];
var _rot = _data[1];
var _sca = _data[2];
var _anc = _data[3];
object.transform.position.set( _pos[0], _pos[1], _pos[2]);
object.transform.anchor.set( _anc[0], _anc[1], _anc[2]);
object.transform.rotation.set( _rot[0], _rot[1], _rot[2], _rot[3]);
object.transform.scale.set( _sca[0], _sca[1], _sca[2]);
return object;
} #endregion
static getObject = function(index, class = object_class) { #region
var _obj = array_safe_get_fast(cached_object, index, noone);
if(_obj == noone) {
_obj = new class();
} else if(!is_instanceof(_obj, class)) {
_obj.destroy();
_obj = new class();
}
cached_object[index] = _obj;
return _obj;
} #endregion
}

View file

@ -0,0 +1,633 @@
// 2024-05-01 16:28:38
function Node_3D_Object(_x, _y, _group = noone) : Node_3D(_x, _y, _group) constructor {
name = "3D Object";
cached_object = [];
object_class = noone;
preview_channel = 0;
inputs[| 0] = nodeValue("Position", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, [ 0, 0, 0 ])
.setDisplay(VALUE_DISPLAY.vector);
inputs[| 1] = nodeValue("Rotation", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, [ 0, 0, 0, 1 ])
.setDisplay(VALUE_DISPLAY.d3quarternion);
inputs[| 2] = nodeValue("Scale", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, [ 1, 1, 1 ])
.setDisplay(VALUE_DISPLAY.vector);
inputs[| 3] = nodeValue("Anchor", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, [ 0, 0, 0 ])
.setDisplay(VALUE_DISPLAY.vector);
in_d3d = ds_list_size(inputs);
#macro __d3d_input_list_transform ["Transform", false], 0, 3, 1, 2
#region ---- overlay ----
drag_axis = noone;
drag_sv = 0;
drag_delta = 0;
drag_prev = 0;
drag_dist = 0;
drag_val = 0;
drag_mx = 0;
drag_my = 0;
drag_px = 0;
drag_py = 0;
drag_cx = 0;
drag_cy = 0;
drag_rot_axis = new BBMOD_Quaternion();
drag_original = 0;
axis_hover = noone;
#endregion
#region ---- tools ----
tool_pos = new NodeTool( "Transform", THEME.tools_3d_transform, "Node_3D_Object" );
tool_rot = new NodeTool( "Rotate", THEME.tools_3d_rotate, "Node_3D_Object" );
tool_sca = new NodeTool( "Scale", THEME.tools_3d_scale, "Node_3D_Object" );
tools = [ tool_pos, tool_rot, tool_sca ];
tool_axis_edit = new scrollBox([ "local", "global" ], function(val) { tool_attribute.context = val; });
// tool_axis_edit.font = f_p2;
// tool_axis_edit.arrow_spr = THEME.arrow;
// tool_axis_edit.arrow_ind = 3;
tool_attribute.context = 0;
tool_settings = [
[ "Axis", tool_axis_edit, "context", tool_attribute ],
];
static getToolSettings = function() {
if(isUsingTool("Transform") || isUsingTool("Rotate"))
return tool_settings;
return [];
}
#endregion
static drawGizmoPosition = function(index, object, _vpos, active, params, _mx, _my, _snx, _sny, _panel) { #region
#region ---- main ----
var _pos = inputs[| index].getValue(,,, true);
var _qrot = object == noone? new BBMOD_Quaternion() : object.transform.rotation;
var _qinv = new BBMOD_Quaternion().FromAxisAngle(new BBMOD_Vec3(1, 0, 0), 90);
var _camera = params.camera;
var _qview = new BBMOD_Quaternion().FromEuler(_camera.focus_angle_y, -_camera.focus_angle_x, 0);
var _axis = tool_attribute.context;
var _hover = noone;
var _hoverDist = 10;
var th;
var _posView = _camera.worldPointToViewPoint(_vpos);
var cx = _posView.x;
var cy = _posView.y;
var ga = [];
var size = 64;
var hs = size / 2;
var sq = 8;
#endregion
#region display
ga[0] = new BBMOD_Vec3(-size, 0, 0);
ga[1] = new BBMOD_Vec3(0, 0, size);
ga[2] = new BBMOD_Vec3(0, -size, 0);
ga[3] = [ new BBMOD_Vec3(-hs + sq, 0, hs - sq),
new BBMOD_Vec3(-hs - sq, 0, hs - sq),
new BBMOD_Vec3(-hs - sq, 0, hs + sq),
new BBMOD_Vec3(-hs + sq, 0, hs + sq), ];
ga[4] = [ new BBMOD_Vec3( 0, -hs + sq, hs - sq),
new BBMOD_Vec3( 0, -hs - sq, hs - sq),
new BBMOD_Vec3( 0, -hs - sq, hs + sq),
new BBMOD_Vec3( 0, -hs + sq, hs + sq), ];
ga[5] = [ new BBMOD_Vec3(-hs + sq, -hs - sq, 0),
new BBMOD_Vec3(-hs - sq, -hs - sq, 0),
new BBMOD_Vec3(-hs - sq, -hs + sq, 0),
new BBMOD_Vec3(-hs + sq, -hs + sq, 0), ];
ga[0] = new BBMOD_Vec3(-size, 0, 0);
ga[1] = new BBMOD_Vec3(0, -size, 0);
ga[2] = new BBMOD_Vec3(0, 0, -size);
ga[3] = [ new BBMOD_Vec3(-hs + sq, -hs - sq, 0),
new BBMOD_Vec3(-hs - sq, -hs - sq, 0),
new BBMOD_Vec3(-hs - sq, -hs + sq, 0),
new BBMOD_Vec3(-hs + sq, -hs + sq, 0), ];
ga[4] = [ new BBMOD_Vec3( 0, -hs + sq, -hs - sq),
new BBMOD_Vec3( 0, -hs - sq, -hs - sq),
new BBMOD_Vec3( 0, -hs - sq, -hs + sq),
new BBMOD_Vec3( 0, -hs + sq, -hs + sq), ];
ga[5] = [ new BBMOD_Vec3(-hs + sq, 0, -hs - sq),
new BBMOD_Vec3(-hs - sq, 0, -hs - sq),
new BBMOD_Vec3(-hs - sq, 0, -hs + sq),
new BBMOD_Vec3(-hs + sq, 0, -hs + sq), ];
for( var i = 0; i < 3; i++ ) {
if(_axis == 0)
ga[i] = _qview.Rotate(_qinv.Rotate(_qrot.Rotate(ga[i])));
else if(_axis == 1)
ga[i] = _qview.Rotate(_qinv.Rotate(ga[i]));
th = 2 + (axis_hover == i || drag_axis == i);
if(drag_axis != noone && drag_axis != i)
continue;
draw_set_color(COLORS.axis[i]);
if(point_distance(cx, cy, cx + ga[i].X, cy + ga[i].Y) < 5)
draw_line_round(cx, cy, cx + ga[i].X, cy + ga[i].Y, th);
else
draw_line_round_arrow(cx, cy, cx + ga[i].X, cy + ga[i].Y, th, 3);
var _d = distance_to_line(_mx, _my, cx, cy, cx + ga[i].X, cy + ga[i].Y);
if(_d < _hoverDist) {
_hover = i;
_hoverDist = _d;
}
}
for( var i = 3; i < 6; i++ ) {
for( var j = 0; j < 4; j++ ) {
if(_axis == 0)
ga[i][j] = _qview.Rotate(_qinv.Rotate(_qrot.Rotate(ga[i][j])));
else if(_axis == 1)
ga[i][j] = _qview.Rotate(_qinv.Rotate(ga[i][j]));
}
th = 1;
var p0x = cx + ga[i][0].X, p0y = cy + ga[i][0].Y;
var p1x = cx + ga[i][1].X, p1y = cy + ga[i][1].Y;
var p2x = cx + ga[i][2].X, p2y = cy + ga[i][2].Y;
var p3x = cx + ga[i][3].X, p3y = cy + ga[i][3].Y;
var _pax = (p0x + p1x + p2x + p3x) / 4;
var _pay = (p0y + p1y + p2y + p3y) / 4;
if((abs(p0x - _pax) + abs(p1x - _pax) + abs(p2x - _pax) + abs(p3x - _pax)) / 4 < 1)
continue;
if((abs(p0y - _pay) + abs(p1y - _pay) + abs(p2y - _pay) + abs(p3y - _pay)) / 4 < 1)
continue;
draw_set_color(COLORS.axis[(i - 3 - 1 + 3) % 3]);
if(axis_hover == i || drag_axis == i) {
draw_primitive_begin(pr_trianglestrip);
draw_vertex(p0x, p0y);
draw_vertex(p1x, p1y);
draw_vertex(p3x, p3y);
draw_vertex(p2x, p2y);
draw_primitive_end();
} else if (drag_axis == noone) {
draw_line(p0x, p0y, p1x, p1y);
draw_line(p1x, p1y, p2x, p2y);
draw_line(p2x, p2y, p3x, p3y);
draw_line(p3x, p3y, p0x, p0y);
} else
continue;
if(point_in_rectangle_points(_mx, _my, p0x, p0y, p1x, p1y, p3x, p3y, p2x, p2y))
_hover = i;
}
axis_hover = _hover;
#endregion display
if(drag_axis != noone) { #region editing
if(!MOUSE_WRAPPING) {
drag_mx += _mx - drag_px;
drag_my += _my - drag_py;
var mAdj, nor, prj;
var ray = _camera.viewPointToWorldRay(drag_mx, drag_my);
var val = [ drag_val[0], drag_val[1], drag_val[2] ];
if(drag_axis < 3) {
switch(drag_axis) {
case 0 : nor = new __vec3(0, 1, 0); prj = new __vec3(1, 0, 0); break;
case 1 : nor = new __vec3(0, 0, 1); prj = new __vec3(0, 1, 0); break;
case 2 : nor = new __vec3(1, 0, 0); prj = new __vec3(0, 0, 1); break;
}
if(_axis == 0) {
nor = _qrot.Rotate(nor);
prj = _qrot.Rotate(prj);
}
var pln = new __plane(drag_original, nor);
mAdj = d3d_intersect_ray_plane(ray, pln);
if(drag_prev != undefined) {
var _diff = mAdj.subtract(drag_prev);
var _dist = _diff.dot(prj);
for( var i = 0; i < 3; i++ )
val[i] += prj.getIndex(i) * _dist;
if(inputs[| index].setValue(value_snap(val, _snx)))
UNDO_HOLDING = true;
}
} else {
switch(drag_axis) {
case 3 : nor = new __vec3(0, 0, 1); break;
case 4 : nor = new __vec3(1, 0, 0); break;
case 5 : nor = new __vec3(0, 1, 0); break;
}
if(_axis == 0)
nor = _qrot.Rotate(nor);
var pln = new __plane(drag_original, nor);
mAdj = d3d_intersect_ray_plane(ray, pln);
if(drag_prev != undefined) {
var _diff = mAdj.subtract(drag_prev);
for( var i = 0; i < 3; i++ )
val[i] += _diff.getIndex(i);
if(inputs[| index].setValue(value_snap(val, _snx)))
UNDO_HOLDING = true;
}
}
drag_val = [ val[0], val[1], val[2] ];
drag_prev = mAdj;
}
setMouseWrap();
drag_px = _mx;
drag_py = _my;
} #endregion
if(_hover != noone && mouse_press(mb_left, active)) { #region
drag_axis = _hover;
drag_prev = undefined;
drag_mx = _mx;
drag_my = _my;
drag_px = _mx;
drag_py = _my;
drag_cx = cx;
drag_cy = cy;
drag_val = _pos;
drag_original = new __vec3(_pos);
} #endregion
} #endregion
static drawGizmoRotation = function(index, object, _vpos, active, params, _mx, _my, _snx, _sny, _panel) { #region
#region ---- main ----
var _rot = inputs[| index].getValue();
var _qrot = object == noone? new BBMOD_Quaternion() : object.transform.rotation;
var _qinv = new BBMOD_Quaternion().FromAxisAngle(new BBMOD_Vec3(1, 0, 0), 90);
var _camera = params.camera;
var _qview = new BBMOD_Quaternion().FromEuler(_camera.focus_angle_y, -_camera.focus_angle_x, 0);
var _axis = tool_attribute.context;
var _hover = noone;
var _hoverDist = 10;
var th;
var _posView = _camera.worldPointToViewPoint(_vpos);
var cx = _posView.x;
var cy = _posView.y;
var ga = [];
var size = 64;
var hs = size / 2;
var sq = 8;
#endregion
#region display
var size = 64;
for( var i = 0; i < 3; i++ ) {
var op, np;
th = 2 + (axis_hover == i || drag_axis == i);
if(drag_axis != noone && drag_axis != i)
continue;
draw_set_color(COLORS.axis[i]);
for( var j = 0; j <= 32; j++ ) {
var ang = j / 32 * 360;
switch(i) {
case 0 : np = new BBMOD_Vec3(0, lengthdir_x(size, ang), lengthdir_y(size, ang)); break;
case 1 : np = new BBMOD_Vec3(lengthdir_x(size, ang), lengthdir_y(size, ang), 0); break;
case 2 : np = new BBMOD_Vec3(lengthdir_x(size, ang), 0, lengthdir_y(size, ang)); break;
}
if(_axis == 0)
np = _qview.Rotate(_qinv.Rotate(_qrot.Rotate(np)));
else if(_axis == 1)
np = _qview.Rotate(_qinv.Rotate(np));
if(j && (op.Z > 0 && np.Z > 0 || drag_axis == i)) {
draw_line_round(cx + op.X, cy + op.Y, cx + np.X, cy + np.Y, th);
var _d = distance_to_line(_mx, _my, cx + op.X, cy + op.Y, cx + np.X, cy + np.Y);
if(_d < _hoverDist) {
_hover = i;
_hoverDist = _d;
}
}
op = np;
}
}
axis_hover = _hover;
#endregion
if(drag_axis != noone) { #region
var mAng = point_direction(cx, cy, _mx, _my);
if(drag_rot_axis == undefined) {
drag_rot_axis = BBMOD_VEC3_FORWARD;
switch(drag_axis) {
case 0 : drag_rot_axis = new BBMOD_Vec3(-1, 0, 0); break;
case 1 : drag_rot_axis = new BBMOD_Vec3( 0, 0, -1); break;
case 2 : drag_rot_axis = new BBMOD_Vec3( 0, -1, 0); break;
}
if(_axis == 0) drag_rot_axis = _qrot.Rotate(drag_rot_axis).Normalize();
}
var _nv = _qview.Rotate(_qinv.Rotate(drag_rot_axis));
draw_line_round(cx, cy, cx + _nv.X * 100, cy + _nv.Y * 100, 2);
if(drag_prev != undefined) {
var _rd = (mAng - drag_prev) * (_nv.Z > 0? 1 : -1);
drag_dist += _rd;
var _dist = value_snap(drag_dist, _sny);
var _currR = new BBMOD_Quaternion().FromAxisAngle(drag_rot_axis, _dist);
var _val = _currR.Mul(drag_val);
var _Nrot = _val.ToArray();
if(inputs[| index].setValue(_Nrot))
UNDO_HOLDING = true;
}
draw_set_color(COLORS._main_accent);
draw_line_dashed(cx, cy, _mx, _my, 1, 4);
drag_prev = mAng;
} #endregion
if(_hover != noone && mouse_press(mb_left, active)) { #region
drag_axis = _hover;
drag_prev = undefined;
drag_val = _qrot.Clone();
drag_dist = 0;
drag_rot_axis = undefined;
} #endregion
} #endregion
static drawGizmoScale = function(index, object, _vpos, active, params, _mx, _my, _snx, _sny, _panel) { #region
tool_attribute.context = 0;
#region ---- main ----
var _sca = inputs[| index].getValue(,,, true);
var _qrot = object == noone? new BBMOD_Quaternion() : object.transform.rotation;
var _qinv = new BBMOD_Quaternion().FromAxisAngle(new BBMOD_Vec3(1, 0, 0), 90);
var _camera = params.camera;
var _qview = new BBMOD_Quaternion().FromEuler(_camera.focus_angle_y, -_camera.focus_angle_x, 0);
var _axis = tool_attribute.context;
var _hover = noone;
var _hoverDist = 10;
var th;
var _posView = _camera.worldPointToViewPoint(_vpos);
var cx = _posView.x;
var cy = _posView.y;
var ga = [];
var size = 64;
var hs = size / 2;
var sq = 8;
#endregion
#region display
var ga = [];
var size = 64;
var hs = size / 2;
var sq = 8;
ga[0] = new BBMOD_Vec3(-size, 0, 0);
ga[1] = new BBMOD_Vec3(0, -size, 0);
ga[2] = new BBMOD_Vec3(0, 0, -size);
ga[3] = [ new BBMOD_Vec3(-hs + sq, -hs - sq, 0),
new BBMOD_Vec3(-hs - sq, -hs - sq, 0),
new BBMOD_Vec3(-hs - sq, -hs + sq, 0),
new BBMOD_Vec3(-hs + sq, -hs + sq, 0), ];
ga[4] = [ new BBMOD_Vec3( 0, -hs + sq, -hs - sq),
new BBMOD_Vec3( 0, -hs - sq, -hs - sq),
new BBMOD_Vec3( 0, -hs - sq, -hs + sq),
new BBMOD_Vec3( 0, -hs + sq, -hs + sq), ];
ga[5] = [ new BBMOD_Vec3(-hs + sq, 0, -hs - sq),
new BBMOD_Vec3(-hs - sq, 0, -hs - sq),
new BBMOD_Vec3(-hs - sq, 0, -hs + sq),
new BBMOD_Vec3(-hs + sq, 0, -hs + sq), ];
for( var i = 0; i < 3; i++ ) {
ga[i] = _qview.Rotate(_qinv.Rotate(_qrot.Rotate(ga[i])));
th = 2 + (axis_hover == i || drag_axis == i);
if(drag_axis != noone && drag_axis != i)
continue;
draw_set_color(COLORS.axis[i]);
if(point_distance(cx, cy, cx + ga[i].X, cy + ga[i].Y) < 5)
draw_line_round(cx, cy, cx + ga[i].X, cy + ga[i].Y, th);
else
draw_line_round_arrow_block(cx, cy, cx + ga[i].X, cy + ga[i].Y, th, 3);
var _d = distance_to_line(_mx, _my, cx, cy, cx + ga[i].X, cy + ga[i].Y);
if(_d < _hoverDist) {
_hover = i;
_hoverDist = _d;
}
}
for( var i = 3; i < 6; i++ ) {
for( var j = 0; j < 4; j++ ) {
if(_axis == 0)
ga[i][j] = _qview.Rotate(_qinv.Rotate(_qrot.Rotate(ga[i][j])));
else
ga[i][j] = _qview.Rotate(_qinv.Rotate(ga[i][j]));
}
th = 1;
var p0x = cx + ga[i][0].X, p0y = cy + ga[i][0].Y;
var p1x = cx + ga[i][1].X, p1y = cy + ga[i][1].Y;
var p2x = cx + ga[i][2].X, p2y = cy + ga[i][2].Y;
var p3x = cx + ga[i][3].X, p3y = cy + ga[i][3].Y;
var _pax = (p0x + p1x + p2x + p3x) / 4;
var _pay = (p0y + p1y + p2y + p3y) / 4;
if((abs(p0x - _pax) + abs(p1x - _pax) + abs(p2x - _pax) + abs(p3x - _pax)) / 4 < 1)
continue;
if((abs(p0y - _pay) + abs(p1y - _pay) + abs(p2y - _pay) + abs(p3y - _pay)) / 4 < 1)
continue;
draw_set_color(COLORS.axis[(i - 3 - 1 + 3) % 3]);
if(axis_hover == i || drag_axis == i) {
draw_primitive_begin(pr_trianglestrip);
draw_vertex(p0x, p0y);
draw_vertex(p1x, p1y);
draw_vertex(p3x, p3y);
draw_vertex(p2x, p2y);
draw_primitive_end();
} else if (drag_axis == noone) {
draw_line(p0x, p0y, p1x, p1y);
draw_line(p1x, p1y, p2x, p2y);
draw_line(p2x, p2y, p3x, p3y);
draw_line(p3x, p3y, p0x, p0y);
} else
continue;
if(point_in_rectangle_points(_mx, _my, p0x, p0y, p1x, p1y, p3x, p3y, p2x, p2y))
_hover = i;
}
axis_hover = _hover;
#endregion
if(drag_axis != noone) { #region editing
if(!MOUSE_WRAPPING) {
drag_mx += _mx - drag_px;
drag_my += _my - drag_py;
var mAdj, nor, prj;
var ray = _camera.viewPointToWorldRay(drag_mx, drag_my);
if(drag_axis < 3) {
switch(drag_axis) {
case 0 : nor = new __vec3(0, 1, 0); prj = new __vec3(1, 0, 0); break;
case 1 : nor = new __vec3(0, 0, 1); prj = new __vec3(0, 1, 0); break;
case 2 : nor = new __vec3(1, 0, 0); prj = new __vec3(0, 0, 1); break;
}
nor = _qrot.Rotate(nor);
var pln = new __plane(drag_original, nor);
mAdj = d3d_intersect_ray_plane(ray, pln);
if(drag_prev != undefined) {
var _diff = mAdj.subtract(drag_prev);
var _dist = _diff.dot(prj);
drag_val[drag_axis] += prj.getIndex(drag_axis) * _dist;
if(inputs[| index].setValue(value_snap(drag_val, _snx)))
UNDO_HOLDING = true;
}
} else {
switch(drag_axis) {
case 3 : nor = new __vec3(0, 0, 1); break;
case 4 : nor = new __vec3(1, 0, 0); break;
case 5 : nor = new __vec3(0, 1, 0); break;
}
nor = _qrot.Rotate(nor);
var pln = new __plane(drag_original, nor);
mAdj = d3d_intersect_ray_plane(ray, pln);
if(drag_prev != undefined) {
var _diff = mAdj.subtract(drag_prev);
for( var i = 0; i < 3; i++ )
drag_val[i] += _diff.getIndex(i);
if(inputs[| index].setValue(value_snap(drag_val, _snx)))
UNDO_HOLDING = true;
}
}
drag_prev = mAdj;
}
setMouseWrap();
drag_px = _mx;
drag_py = _my;
} #endregion
if(_hover != noone && mouse_press(mb_left, active)) { #region
drag_axis = _hover;
drag_prev = undefined;
drag_mx = _mx;
drag_my = _my;
drag_px = _mx;
drag_py = _my;
drag_cx = cx;
drag_cy = cy;
drag_val = [ _sca[0], _sca[1], _sca[2] ];
drag_original = new __vec3(_sca);
} #endregion
} #endregion
static drawOverlay3D = function(active, params, _mx, _my, _snx, _sny, _panel) { #region
var object = getPreviewObjects();
if(array_empty(object)) return;
object = object[0];
var _pos = inputs[| 0].getValue(,,, true);
var _vpos = new __vec3( _pos[0], _pos[1], _pos[2] );
if(isUsingTool("Transform")) drawGizmoPosition(0, object, _vpos, active, params, _mx, _my, _snx, _sny, _panel);
else if(isUsingTool("Rotate")) drawGizmoRotation(1, object, _vpos, active, params, _mx, _my, _snx, _sny, _panel);
else if(isUsingTool("Scale")) drawGizmoScale(2, object, _vpos, active, params, _mx, _my, _snx, _sny, _panel);
if(drag_axis != noone && mouse_release(mb_left)) {
drag_axis = noone;
UNDO_HOLDING = false;
}
if(onDrawOverlay3D != 0) onDrawOverlay3D(active, params, _mx, _my, _snx, _sny, _panel);
} #endregion
static onDrawOverlay3D = 0;
static setTransform = function(object, _data) { #region
if(object == noone) return;
var _pos = _data[0];
var _rot = _data[1];
var _sca = _data[2];
var _anc = _data[3];
object.transform.position.set( _pos[0], _pos[1], _pos[2]);
object.transform.anchor.set( _anc[0], _anc[1], _anc[2]);
object.transform.rotation.set( _rot[0], _rot[1], _rot[2], _rot[3]);
object.transform.scale.set( _sca[0], _sca[1], _sca[2]);
return object;
} #endregion
static getObject = function(index, class = object_class) { #region
var _obj = array_safe_get_fast(cached_object, index, noone);
if(_obj == noone) {
_obj = new class();
} else if(!is_instanceof(_obj, class)) {
_obj.destroy();
_obj = new class();
}
cached_object[index] = _obj;
return _obj;
} #endregion
}

View file

@ -1,4 +1,4 @@
// 2024-05-01 08:40:49 // 2024-05-01 08:42:42
function colorSelector(onApply = noone) constructor { function colorSelector(onApply = noone) constructor {
self.onApply = onApply; self.onApply = onApply;

View file

@ -1,4 +1,4 @@
// 2024-05-01 08:40:09 // 2024-05-01 08:42:38
function colorSelector(onApply = noone) constructor { function colorSelector(onApply = noone) constructor {
self.onApply = onApply; self.onApply = onApply;

View file

@ -0,0 +1,23 @@
// 2024-05-01 15:56:10
function __bbox3D(first, second) constructor {
self.first = first;
self.second = second;
static getScale = function() {
INLINE
return sqrt(
sqr(first.x - second.x) +
sqr(first.y - second.y) +
sqr(first.z - second.z)
);
}
static getMaximumScale = function() {
INLINE
return max(
abs(first.x - second.x),
abs(first.y - second.y),
abs(first.z - second.z),
);
}
}

View file

@ -0,0 +1,23 @@
// 2024-05-01 15:56:08
function __bbox3D(first, second) constructor {
self.first = first;
self.second = second;
static getScale = function() {
INLINE
return sqrt(
sqr(first.x - second.x) +
sqr(first.y - second.y) +
sqr(first.z - second.z)
);
}
static getMaximumScale = function() {
INLINE
return max(
abs(first.x - second.x),
abs(first.y - second.y),
abs(first.z - second.z),
);
}
}

View file

@ -0,0 +1,253 @@
// 2024-05-01 15:54:59
#region vertex format
vertex_format_begin();
vertex_format_add_position_3d();
vertex_format_add_color();
global.VF_POS_COL = vertex_format_end();
vertex_format_begin();
vertex_format_add_position_3d();
vertex_format_add_normal();
vertex_format_add_texcoord();
vertex_format_add_color();
global.VF_POS_NORM_TEX_COL = vertex_format_end();
global.VF_POS_NORM_TEX_COL_size = 36;
#endregion
function __3dObject() constructor {
vertex = [];
normal_vertex = [];
object_counts = 1;
VB = [];
NVB = noone;
normal_draw_size = 0.2;
VF = global.VF_POS_COL;
render_type = pr_trianglelist;
custom_shader = noone;
transform = new __transform();
size = new __vec3(1);
materials = [];
material_index = [];
texture_flip = false;
static checkParameter = function(params = {}, forceUpdate = false) { #region
var _keys = struct_get_names(params);
var check = false;
for( var i = 0, n = array_length(_keys); i < n; i++ ) {
var key = _keys[i];
if(self[$ key] != params[$ key])
check = true;
self[$ key] = params[$ key];
}
if(forceUpdate || check) onParameterUpdate();
} #endregion
static onParameterUpdate = function() {}
static generateNormal = function(_s = normal_draw_size) { #region
if(render_type != pr_trianglelist) return;
NVB = array_create(object_counts);
for( var i = 0; i < object_counts; i++ ) {
NVB[i] = vertex_create_buffer();
vertex_begin(NVB[i], global.VF_POS_COL);
for( var j = 0, n = array_length(vertex[i]); j < n; j++ ) {
var _v = vertex[i][j];
vertex_position_3d(NVB[i], _v.x, _v.y, _v.z);
vertex_color(NVB[i], c_red, 1);
vertex_position_3d(NVB[i], _v.x + _v.nx * _s, _v.y + _v.ny * _s, _v.z + _v.nz * _s);
vertex_color(NVB[i], c_red, 1);
}
vertex_end(NVB[i]);
}
} #endregion
static buildVertex = function(_vertex) { #region
var _buffer = vertex_create_buffer();
vertex_begin(_buffer, VF);
for( var i = 0, n = array_length(_vertex); i < n; i++ ) {
var v = _vertex[i];
switch(VF) {
case global.VF_POS_COL : vertex_add_vc(_buffer, v); break;
case global.VF_POS_NORM_TEX_COL : vertex_add_vntc(_buffer, v); break;
}
}
vertex_end(_buffer);
//vertex_freeze(_buffer);
return _buffer;
} #endregion
static build = function(_buffer = VB, _vertex = vertex, counts = object_counts) { #region
if(is_array(_buffer)) {
for( var i = 0, n = array_length(_buffer); i < n; i++ )
if(_buffer[i] != noone) vertex_delete_buffer(_buffer[i])
} else if(_buffer != noone) vertex_delete_buffer(_buffer);
if(array_empty(_vertex)) return noone;
var _res = array_create(counts);
for( var i = 0; i < counts; i++ )
_res[i] = buildVertex(_vertex[i]);
return _res;
} #endregion
static preSubmitVertex = function(scene = {}) {}
static postSubmitVertex = function(scene = {}) {}
static getCenter = function() { return new __vec3(transform.position.x, transform.position.y, transform.position.z); }
static getBBOX = function() { return new __bbox3D(size.multiplyVec(transform.scale).multiply(-0.5), size.multiplyVec(transform.scale).multiply(0.5)); }
static submit = function(scene = {}, shader = noone) { submitVertex(scene, shader); }
static submitUI = function(scene = {}, shader = noone) { submitVertex(scene, shader); }
static submitSel = function(scene = {}, shader = noone) { #region
var _s = variable_clone(scene);
_s.show_normal = false;
submitVertex(_s, sh_d3d_silhouette);
} #endregion
static submitShader = function(scene = {}, shader = noone) {}
static submitShadow = function(scene = {}, object = noone) {}
static submitVertex = function(scene = {}, shader = noone) { #region
var _shader = sh_d3d_default;
switch(VF) {
case global.VF_POS_NORM_TEX_COL: _shader = sh_d3d_default; break;
case global.VF_POS_COL: _shader = sh_d3d_wireframe; break;
}
if(custom_shader != noone) _shader = custom_shader;
if(shader != noone) _shader = shader;
shader_set(_shader);
preSubmitVertex(scene);
transform.submitMatrix();
matrix_set(matrix_world, matrix_stack_top());
#region ++++ Submit & Material ++++
gpu_set_tex_repeat(true);
for( var i = 0, n = array_length(VB); i < n; i++ ) {
var _ind = array_safe_get_fast(material_index, i, i);
var _mat = array_safe_get_fast(materials, _ind, noone);
var _useMat = is_instanceof(_mat, __d3dMaterial);
shader_set_i("mat_flip", texture_flip);
var _tex = _useMat? _mat.getTexture() : -1;
if(_shader == sh_d3d_default) {
if(_useMat) {
_mat.submitShader();
} else {
shader_set_f("mat_diffuse", 1);
shader_set_f("mat_specular", 0);
shader_set_f("mat_shine", 1);
shader_set_i("mat_metalic", 0);
shader_set_f("mat_reflective", 0);
}
vertex_submit(VB[i], render_type, _tex);
} else if(_shader == sh_d3d_geometry) {
if(_useMat)
_mat.submitGeometry();
else
shader_set_i("use_normal", 0);
vertex_submit(VB[i], render_type, _tex);
} else
vertex_submit(VB[i], render_type, _tex);
// print($"Submit vertex ({scene}) [{VB[i]}: {vertex_get_buffer_size(VB[i])}]");
}
gpu_set_tex_repeat(false);
#endregion
shader_reset();
if(scene.show_normal) { #region
if(NVB == noone) generateNormal();
if(NVB != noone) {
shader_set(sh_d3d_wireframe);
shader_set_color("blend", c_white);
for( var i = 0, n = array_length(NVB); i < n; i++ )
vertex_submit(NVB[i], pr_linelist, -1);
shader_reset();
}
} #endregion
transform.clearMatrix();
matrix_set(matrix_world, matrix_build_identity());
postSubmitVertex(scene);
} #endregion
static clone = function(_vertex = true, cloneBuffer = false) { #region
var _obj = new __3dObject();
if(_vertex) {
_obj.vertex = array_create(array_length(vertex));
for( var i = 0, n = array_length(vertex); i < n; i++ ) {
_obj.vertex[i] = array_create(array_length(vertex[i]));
for( var j = 0, m = array_length(vertex[i]); j < m; j++ )
_obj.vertex[i][j] = vertex[i][j].clone();
}
}
if(cloneBuffer) {
_obj.VB = array_create(array_length(VB));
for( var i = 0, n = array_length(VB); i < n; i++ ) {
var _vnum = vertex_get_number(VB[i]);
var _buff = buffer_create(1, buffer_grow, 1);
buffer_copy_from_vertex_buffer(VB[i], 0, _vnum - 1, _buff, 0);
_obj.VB[i] = vertex_create_buffer_from_buffer(_buff, VF);
}
} else {
_obj.VB = VB;
}
_obj.NVB = NVB;
_obj.VF = VF;
_obj.render_type = render_type;
_obj.custom_shader = custom_shader;
_obj.object_counts = object_counts;
_obj.transform = transform.clone();
_obj.size = size.clone();
_obj.materials = materials;
_obj.material_index = material_index;
_obj.texture_flip = texture_flip;
return _obj;
} #endregion
static destroy = function() { #region
for( var i = 0, n = array_length(VB); i < n; i++ )
vertex_delete_buffer(VB[i]);
VB = [];
onDestroy();
} #endregion
static onDestroy = function() { }
static toString = function() { return $"[D3D Object]\n\t({array_length(vertex)} vertex groups\n\tPosition: {transform.position}\n\tRotation: {transform.rotation}\n\tScale: {transform.scale})" }
}

View file

@ -0,0 +1,253 @@
// 2024-05-01 15:51:30
#region vertex format
vertex_format_begin();
vertex_format_add_position_3d();
vertex_format_add_color();
global.VF_POS_COL = vertex_format_end();
vertex_format_begin();
vertex_format_add_position_3d();
vertex_format_add_normal();
vertex_format_add_texcoord();
vertex_format_add_color();
global.VF_POS_NORM_TEX_COL = vertex_format_end();
global.VF_POS_NORM_TEX_COL_size = 36;
#endregion
function __3dObject() constructor {
vertex = [];
normal_vertex = [];
object_counts = 1;
VB = [];
NVB = noone;
normal_draw_size = 0.2;
VF = global.VF_POS_COL;
render_type = pr_trianglelist;
custom_shader = noone;
transform = new __transform();
size = new __vec3(1);
materials = [];
material_index = [];
texture_flip = false;
static checkParameter = function(params = {}, forceUpdate = false) { #region
var _keys = struct_get_names(params);
var check = false;
for( var i = 0, n = array_length(_keys); i < n; i++ ) {
var key = _keys[i];
if(self[$ key] != params[$ key])
check = true;
self[$ key] = params[$ key];
}
if(forceUpdate || check) onParameterUpdate();
} #endregion
static onParameterUpdate = function() {}
static generateNormal = function(_s = normal_draw_size) { #region
if(render_type != pr_trianglelist) return;
NVB = array_create(object_counts);
for( var i = 0; i < object_counts; i++ ) {
NVB[i] = vertex_create_buffer();
vertex_begin(NVB[i], global.VF_POS_COL);
for( var j = 0, n = array_length(vertex[i]); j < n; j++ ) {
var _v = vertex[i][j];
vertex_position_3d(NVB[i], _v.x, _v.y, _v.z);
vertex_color(NVB[i], c_red, 1);
vertex_position_3d(NVB[i], _v.x + _v.nx * _s, _v.y + _v.ny * _s, _v.z + _v.nz * _s);
vertex_color(NVB[i], c_red, 1);
}
vertex_end(NVB[i]);
}
} #endregion
static buildVertex = function(_vertex) { #region
var _buffer = vertex_create_buffer();
vertex_begin(_buffer, VF);
for( var i = 0, n = array_length(_vertex); i < n; i++ ) {
var v = _vertex[i];
switch(VF) {
case global.VF_POS_COL : vertex_add_vc(_buffer, v); break;
case global.VF_POS_NORM_TEX_COL : vertex_add_vntc(_buffer, v); break;
}
}
vertex_end(_buffer);
//vertex_freeze(_buffer);
return _buffer;
} #endregion
static build = function(_buffer = VB, _vertex = vertex, counts = object_counts) { #region
if(is_array(_buffer)) {
for( var i = 0, n = array_length(_buffer); i < n; i++ )
if(_buffer[i] != noone) vertex_delete_buffer(_buffer[i])
} else if(_buffer != noone) vertex_delete_buffer(_buffer);
if(array_empty(_vertex)) return noone;
var _res = array_create(counts);
for( var i = 0; i < counts; i++ )
_res[i] = buildVertex(_vertex[i]);
return _res;
} #endregion
static preSubmitVertex = function(scene = {}) {}
static postSubmitVertex = function(scene = {}) {}
static getCenter = function() { return new __vec3(transform.position.x, transform.position.y, transform.position.z); }
static getBBOX = function() { return new __bbox3D(size.multiplyVec(transform.scale).multiply(-0.5), size.multiplyVec(transform.scale).multiply(0.5)); }
static submit = function(scene = {}, shader = noone) { submitVertex(scene, shader); }
static submitUI = function(scene = {}, shader = noone) { submitVertex(scene, shader); }
static submitSel = function(scene = {}, shader = noone) { #region
var _s = variable_clone(scene);
_s.show_normal = false;
submitVertex(_s, sh_d3d_silhouette);
} #endregion
static submitShader = function(scene = {}, shader = noone) {}
static submitShadow = function(scene = {}, object = noone) {}
static submitVertex = function(scene = {}, shader = noone) { #region
var _shader = sh_d3d_default;
switch(VF) {
case global.VF_POS_NORM_TEX_COL: _shader = sh_d3d_default; break;
case global.VF_POS_COL: _shader = sh_d3d_wireframe; break;
}
if(custom_shader != noone) _shader = custom_shader;
if(shader != noone) _shader = shader;
shader_set(_shader);
preSubmitVertex(scene);
transform.submitMatrix();
matrix_set(matrix_world, matrix_stack_top());
#region ++++ Submit & Material ++++
gpu_set_tex_repeat(true);
for( var i = 0, n = array_length(VB); i < n; i++ ) {
var _ind = array_safe_get_fast(material_index, i, i);
var _mat = array_safe_get_fast(materials, _ind, noone);
var _useMat = is_instanceof(_mat, __d3dMaterial);
shader_set_i("mat_flip", texture_flip);
var _tex = _useMat? _mat.getTexture() : -1;
if(_shader == sh_d3d_default) {
if(_useMat) {
_mat.submitShader();
} else {
shader_set_f("mat_diffuse", 1);
shader_set_f("mat_specular", 0);
shader_set_f("mat_shine", 1);
shader_set_i("mat_metalic", 0);
shader_set_f("mat_reflective", 0);
}
vertex_submit(VB[i], render_type, _tex);
} else if(_shader == sh_d3d_geometry) {
if(_useMat)
_mat.submitGeometry();
else
shader_set_i("use_normal", 0);
vertex_submit(VB[i], render_type, _tex);
} else
vertex_submit(VB[i], render_type, _tex);
// print($"Submit vertex ({scene}) [{VB[i]}: {vertex_get_buffer_size(VB[i])}]");
}
gpu_set_tex_repeat(false);
#endregion
shader_reset();
if(scene.show_normal) { #region
if(NVB == noone) generateNormal();
if(NVB != noone) {
shader_set(sh_d3d_wireframe);
shader_set_color("blend", c_white);
for( var i = 0, n = array_length(NVB); i < n; i++ )
vertex_submit(NVB[i], pr_linelist, -1);
shader_reset();
}
} #endregion
transform.clearMatrix();
matrix_set(matrix_world, matrix_build_identity());
postSubmitVertex(scene);
} #endregion
static clone = function(_vertex = true, cloneBuffer = false) { #region
var _obj = new __3dObject();
if(_vertex) {
_obj.vertex = array_create(array_length(vertex));
for( var i = 0, n = array_length(vertex); i < n; i++ ) {
_obj.vertex[i] = array_create(array_length(vertex[i]));
for( var j = 0, m = array_length(vertex[i]); j < m; j++ )
_obj.vertex[i][j] = vertex[i][j].clone();
}
}
if(cloneBuffer) {
_obj.VB = array_create(array_length(VB));
for( var i = 0, n = array_length(VB); i < n; i++ ) {
var _vnum = vertex_get_number(VB[i]);
var _buff = buffer_create(1, buffer_grow, 1);
buffer_copy_from_vertex_buffer(VB[i], 0, _vnum - 1, _buff, 0);
_obj.VB[i] = vertex_create_buffer_from_buffer(_buff, VF);
}
} else {
_obj.VB = VB;
}
_obj.NVB = NVB;
_obj.VF = VF;
_obj.render_type = render_type;
_obj.custom_shader = custom_shader;
_obj.object_counts = object_counts;
_obj.transform = transform.clone();
_obj.size = size.clone();
_obj.materials = materials;
_obj.material_index = material_index;
_obj.texture_flip = texture_flip;
return _obj;
} #endregion
static destroy = function() { #region
for( var i = 0, n = array_length(VB); i < n; i++ )
vertex_delete_buffer(VB[i]);
VB = [];
onDestroy();
} #endregion
static onDestroy = function() { }
static toString = function() { return $"[D3D Object]\n\t({array_length(vertex)} vertex groups\n\tPosition: {transform.position}\n\tRotation: {transform.rotation}\n\tScale: {transform.scale})" }
}

View file

@ -1,4 +1,4 @@
// 2024-05-01 08:11:39 // 2024-05-01 08:42:51
#region save #region save
globalvar LOADING, CLONING, CLONING_GROUP; globalvar LOADING, CLONING, CLONING_GROUP;
globalvar CONNECTION_CONFLICT, LOADING_VERSION; globalvar CONNECTION_CONFLICT, LOADING_VERSION;

View file

@ -1,4 +1,4 @@
// 2024-05-01 08:11:28 // 2024-05-01 08:42:50
#region save #region save
globalvar LOADING, CLONING, CLONING_GROUP; globalvar LOADING, CLONING, CLONING_GROUP;
globalvar CONNECTION_CONFLICT, LOADING_VERSION; globalvar CONNECTION_CONFLICT, LOADING_VERSION;

View file

@ -0,0 +1,243 @@
// 2024-05-01 15:53:22
function Node_create_3D_Obj(_x, _y, _group = noone) { #region
var path = "";
if(!LOADING && !APPENDING && !CLONING) {
path = get_open_filename("3d object|*.obj", "");
key_release();
if(path == "") return noone;
}
var node = new Node_3D_Mesh_Obj(_x, _y, _group);
node.setPath(path);
return node;
} #endregion
function Node_create_3D_Obj_path(_x, _y, path) { #region
if(!file_exists_empty(path)) return noone;
var node = new Node_3D_Mesh_Obj(_x, _y, PANEL_GRAPH.getCurrentContext());
node.setPath(path);
return node;
} #endregion
function Node_3D_Mesh_Obj(_x, _y, _group = noone) : Node_3D_Mesh(_x, _y, _group) constructor {
name = "3D Obj";
object = noone;
object_class = __3dObject;
inputs[| in_mesh + 0] = nodeValue("File Path", self, JUNCTION_CONNECT.input, VALUE_TYPE.path, "" )
.setDisplay(VALUE_DISPLAY.path_load, { filter: "3d object|*.obj" })
.rejectArray();
inputs[| in_mesh + 1] = nodeValue("Flip UV", self, JUNCTION_CONNECT.input, VALUE_TYPE.boolean, true, "Flip UV axis, can be use to fix some texture mapping error.")
.rejectArray();
inputs[| in_mesh + 2] = nodeValue("Import Scale", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 1)
.rejectArray();
input_display_list = [
__d3d_input_list_mesh,
__d3d_input_list_transform,
["Object", false], in_mesh + 0, in_mesh + 2,
["Material", false], in_mesh + 1,
]
setIsDynamicInput(1);
obj_reading = false;
obj_raw = noone;
obj_read_progress = 0;
obj_read_prog_sub = 0;
obj_read_prog_tot = 3;
obj_read_time = 0;
current_path = "";
materials = [];
materialNames = [];
materialIndex = [];
use_normal = false;
insp1UpdateTooltip = __txt("Refresh");
insp1UpdateIcon = [ THEME.refresh_icon, 1, COLORS._main_value_positive ];
static onInspector1Update = function() {
current_path = "";
outputs[| 0].setValue(noone);
}
function setPath(path) { inputs[| in_mesh + 0].setValue(path); }
static createNewInput = function(index = -1) { #region
if(index == -1) index = ds_list_size(inputs);
inputs[| index] = nodeValue("Material", self, JUNCTION_CONNECT.input, VALUE_TYPE.d3Material, new __d3dMaterial())
.setVisible(true, true);
} #endregion
static createMaterial = function(m_index) { #region
var index = input_fix_len + m_index;
input_display_list[input_display_len + m_index] = index;
if(index < ds_list_size(inputs)) return;
createNewInput(index);
if(m_index >= array_length(materials)) return;
var matY = y - (array_length(materials) - 1) / 2 * (128 + 32);
var mat = materials[m_index];
inputs[| index].name = materialNames[m_index] + " Material";
if(file_exists_empty(mat.diff_path)) {
var sol = Node_create_Image_path(x - (w + 128), matY + m_index * (128 + 32), mat.diff_path);
sol.name = mat.name + " texture";
inputs[| index].setFrom(sol.outputs[| 0]);
} else {
var sol = nodeBuild("Node_Solid", x - (w + 128), matY + m_index * (128 + 32));
sol.name = mat.name + " texture";
sol.inputs[| 1].setValue(mat.diff);
inputs[| index].setFrom(sol.outputs[| 0]);
}
} #endregion
static updateObj = function(_path) { #region
if(!file_exists_empty(_path)) return;
current_path = _path;
var _scale = inputs[| in_mesh + 2].getValue();
readObj_init(_scale);
obj_read_time = get_timer();
obj_read_file = file_text_open_read(current_path);
use_display_list = false;
} #endregion
static updateObjProcess = function() { #region
switch(obj_read_progress) {
case 0 : readObj_file(); break;
case 1 : readObj_cent(); break;
case 2 : readObj_buff(); break;
}
} #endregion
static updateObjComplete = function() { #region
use_display_list = true;
if(obj_raw == noone) return;
// var txt = $"========== OBJ import ==========\n";
// txt += $"Vertex counts: {obj_raw.vertex_count}\n";
// txt += $"Object counts: {obj_raw.object_counts}\n";
// txt += $"Material counts: {array_length(obj_raw.materials)}\n";
// txt += $"Model BBOX: {obj_raw.model_size}\n";
// txt += $"Load completed in {(get_timer() - obj_read_time) / 1000} ms\n";
// print(txt);
var span = max(abs(obj_raw.model_size.x), abs(obj_raw.model_size.y), abs(obj_raw.model_size.z));
if(span > 10) noti_warning($"The model is tool large to display properly ({span}u). Scale the model down to preview.");
if(object != noone) object.destroy();
object = new __3dObject();
object.VB = obj_raw.vertex_groups;
object.vertex = obj_raw.vertex;
object.size = obj_raw.model_size;
object.object_counts = obj_raw.object_counts;
use_normal = obj_raw.use_normal;
materialNames = [ "Material" ];
materialIndex = [ 0 ];
materials = [ new MTLmaterial("Material") ];
if(array_length(materialNames)) {
var _dir = filename_dir(current_path);
var _pathMtl = string_copy(current_path, 1, string_length(current_path) - 4) + ".mtl";
if(obj_raw.mtl_path != "") _pathMtl = _dir + "/" + obj_raw.mtl_path;
materials = readMtl(_pathMtl);
if(array_length(materials) == array_length(obj_raw.materials)) {
materialNames = array_create(array_length(materials));
for( var i = 0, n = array_length(materials); i < n; i++ )
materialNames[i] = materials[i].name;
for( var i = 0, n = array_length(materials); i < n; i++ ) {
var _mat = obj_raw.materials[i];
var _ord = array_find(materialNames, _mat);
materialIndex[i] = _ord;
}
} else
noti_warning("Load mtl error: Material amount defined in .mtl file not match the .obj file.")
}
array_resize(input_display_list, input_display_len);
var _overflow = input_fix_len + array_length(materialNames);
while(ds_list_size(inputs) > _overflow)
ds_list_delete(inputs, _overflow);
for(var i = 0; i < array_length(materialNames); i++)
createMaterial(i);
outputs[| 0].setValue(object);
triggerRender();
} #endregion
static step = function() { #region
if(obj_reading) {
updateObjProcess();
if(obj_read_progress == obj_read_prog_tot) {
updateObjComplete();
obj_reading = false;
triggerRender();
}
return;
}
var _path = getInputData(in_mesh + 0);
if(_path != current_path) updateObj(_path);
} #endregion
static processData = function(_output, _data, _output_index, _array_index = 0) { #region
if(obj_reading) return noone;
var _flip = _data[in_mesh + 1];
if(object == noone) return noone;
var materials = [];
for( var i = input_fix_len, n = array_length(_data); i < n; i++ )
materials[i - input_fix_len] = _data[i];
var _object = getObject(_array_index);
_object.VF = global.VF_POS_NORM_TEX_COL;
_object.VB = object.VB;
_object.NVB = object.NVB;
_object.vertex = object.vertex;
_object.size = object.size;
_object.object_counts = object.object_counts;
_object.materials = materials;
_object.material_index = materialIndex;
_object.texture_flip = _flip;
setTransform(_object, _data);
return _object;
} #endregion
static getPreviewValues = function() { return array_safe_get_fast(all_inputs, in_mesh + 2, noone); }
static onDrawNodeOver = function(xx, yy, _mx, _my, _s, _hover, _focus) { #region
if(!obj_reading) return;
var cx = xx + w * _s / 2;
var cy = yy + h * _s / 2;
var rr = min(w - 32, h - 32) * _s / 2;
draw_set_color(COLORS._main_icon);
draw_arc(cx, cy, rr, current_time / 5, current_time / 5 + 90, 4 * _s, 90);
} #endregion
}

View file

@ -0,0 +1,243 @@
// 2024-05-01 15:53:20
function Node_create_3D_Obj(_x, _y, _group = noone) { #region
var path = "";
if(!LOADING && !APPENDING && !CLONING) {
path = get_open_filename("3d object|*.obj", "");
key_release();
if(path == "") return noone;
}
var node = new Node_3D_Mesh_Obj(_x, _y, _group);
node.setPath(path);
return node;
} #endregion
function Node_create_3D_Obj_path(_x, _y, path) { #region
if(!file_exists_empty(path)) return noone;
var node = new Node_3D_Mesh_Obj(_x, _y, PANEL_GRAPH.getCurrentContext());
node.setPath(path);
return node;
} #endregion
function Node_3D_Mesh_Obj(_x, _y, _group = noone) : Node_3D_Mesh(_x, _y, _group) constructor {
name = "3D Obj";
object = noone;
object_class = __3dObject;
inputs[| in_mesh + 0] = nodeValue("File Path", self, JUNCTION_CONNECT.input, VALUE_TYPE.path, "" )
.setDisplay(VALUE_DISPLAY.path_load, { filter: "3d object|*.obj" })
.rejectArray();
inputs[| in_mesh + 1] = nodeValue("Flip UV", self, JUNCTION_CONNECT.input, VALUE_TYPE.boolean, true, "Flip UV axis, can be use to fix some texture mapping error.")
.rejectArray();
inputs[| in_mesh + 2] = nodeValue("Import Scale", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 1)
.rejectArray();
input_display_list = [
__d3d_input_list_mesh,
__d3d_input_list_transform,
["Object", false], in_mesh + 0, in_mesh + 2,
["Material", false], in_mesh + 1,
]
setIsDynamicInput(1);
obj_reading = false;
obj_raw = noone;
obj_read_progress = 0;
obj_read_prog_sub = 0;
obj_read_prog_tot = 3;
obj_read_time = 0;
current_path = "";
materials = [];
materialNames = [];
materialIndex = [];
use_normal = false;
insp1UpdateTooltip = __txt("Refresh");
insp1UpdateIcon = [ THEME.refresh_icon, 1, COLORS._main_value_positive ];
static onInspector1Update = function() {
current_path = "";
outputs[| 0].setValue(noone);
}
function setPath(path) { inputs[| in_mesh + 0].setValue(path); }
static createNewInput = function(index = -1) { #region
if(index == -1) index = ds_list_size(inputs);
inputs[| index] = nodeValue("Material", self, JUNCTION_CONNECT.input, VALUE_TYPE.d3Material, new __d3dMaterial())
.setVisible(true, true);
} #endregion
static createMaterial = function(m_index) { #region
var index = input_fix_len + m_index;
input_display_list[input_display_len + m_index] = index;
if(index < ds_list_size(inputs)) return;
createNewInput(index);
if(m_index >= array_length(materials)) return;
var matY = y - (array_length(materials) - 1) / 2 * (128 + 32);
var mat = materials[m_index];
inputs[| index].name = materialNames[m_index] + " Material";
if(file_exists_empty(mat.diff_path)) {
var sol = Node_create_Image_path(x - (w + 128), matY + m_index * (128 + 32), mat.diff_path);
sol.name = mat.name + " texture";
inputs[| index].setFrom(sol.outputs[| 0]);
} else {
var sol = nodeBuild("Node_Solid", x - (w + 128), matY + m_index * (128 + 32));
sol.name = mat.name + " texture";
sol.inputs[| 1].setValue(mat.diff);
inputs[| index].setFrom(sol.outputs[| 0]);
}
} #endregion
static updateObj = function(_path) { #region
if(!file_exists_empty(_path)) return;
current_path = _path;
var _scale = inputs[| in_mesh + 2].getValue();
readObj_init(_scale);
obj_read_time = get_timer();
obj_read_file = file_text_open_read(current_path);
use_display_list = false;
} #endregion
static updateObjProcess = function() { #region
switch(obj_read_progress) {
case 0 : readObj_file(); break;
case 1 : readObj_cent(); break;
case 2 : readObj_buff(); break;
}
} #endregion
static updateObjComplete = function() { #region
use_display_list = true;
if(obj_raw == noone) return;
// var txt = $"========== OBJ import ==========\n";
// txt += $"Vertex counts: {obj_raw.vertex_count}\n";
// txt += $"Object counts: {obj_raw.object_counts}\n";
// txt += $"Material counts: {array_length(obj_raw.materials)}\n";
// txt += $"Model BBOX: {obj_raw.model_size}\n";
// txt += $"Load completed in {(get_timer() - obj_read_time) / 1000} ms\n";
// print(txt);
var span = max(abs(obj_raw.model_size.x), abs(obj_raw.model_size.y), abs(obj_raw.model_size.z));
if(span > 10) noti_warning($"The model is tool large to display properly ({span}u). Scale the model down to preview.");
if(object != noone) object.destroy();
object = new __3dObject();
object.VB = obj_raw.vertex_groups;
object.vertex = obj_raw.vertex;
object.size = obj_raw.model_size;
object.object_counts = obj_raw.object_counts;
use_normal = obj_raw.use_normal;
materialNames = [ "Material" ];
materialIndex = [ 0 ];
materials = [ new MTLmaterial("Material") ];
if(array_length(materialNames)) {
var _dir = filename_dir(current_path);
var _pathMtl = string_copy(current_path, 1, string_length(current_path) - 4) + ".mtl";
if(obj_raw.mtl_path != "") _pathMtl = _dir + "/" + obj_raw.mtl_path;
materials = readMtl(_pathMtl);
if(array_length(materials) == array_length(obj_raw.materials)) {
materialNames = array_create(array_length(materials));
for( var i = 0, n = array_length(materials); i < n; i++ )
materialNames[i] = materials[i].name;
for( var i = 0, n = array_length(materials); i < n; i++ ) {
var _mat = obj_raw.materials[i];
var _ord = array_find(materialNames, _mat);
materialIndex[i] = _ord;
}
} else
noti_warning("Load mtl error: Material amount defined in .mtl file not match the .obj file.")
}
array_resize(input_display_list, input_display_len);
var _overflow = input_fix_len + array_length(materialNames);
while(ds_list_size(inputs) > _overflow)
ds_list_delete(inputs, _overflow);
for(var i = 0; i < array_length(materialNames); i++)
createMaterial(i);
outputs[| 0].setValue(object);
triggerRender();
} #endregion
static step = function() { #region
if(obj_reading) {
updateObjProcess();
if(obj_read_progress == obj_read_prog_tot) {
updateObjComplete();
obj_reading = false;
triggerRender();
}
return;
}
var _path = getInputData(in_mesh + 0);
if(_path != current_path) updateObj(_path);
} #endregion
static processData = function(_output, _data, _output_index, _array_index = 0) { #region
if(obj_reading) return noone;
var _flip = _data[in_mesh + 1];
if(object == noone) return noone;
var materials = [];
for( var i = input_fix_len, n = array_length(_data); i < n; i++ )
materials[i - input_fix_len] = _data[i];
var _object = getObject(_array_index);
_object.VF = global.VF_POS_NORM_TEX_COL;
_object.VB = object.VB;
_object.NVB = object.NVB;
_object.vertex = object.vertex;
_object.size = object.size;
_object.object_counts = object.object_counts;
_object.materials = materials;
_object.material_index = materialIndex;
_object.texture_flip = _flip;
setTransform(_object, _data);
return _object;
} #endregion
static getPreviewValues = function() { return array_safe_get_fast(all_inputs, in_mesh + 2, noone); }
static onDrawNodeOver = function(xx, yy, _mx, _my, _s, _hover, _focus) { #region
if(!obj_reading) return;
var cx = xx + w * _s / 2;
var cy = yy + h * _s / 2;
var rr = min(w - 32, h - 32) * _s / 2;
draw_set_color(COLORS._main_icon);
draw_arc(cx, cy, rr, current_time / 5, current_time / 5 + 90, 4 * _s, 90);
} #endregion
}

View file

@ -0,0 +1,857 @@
// 2024-05-01 11:49:40
function Node_create_Export(_x, _y, _group = noone) { #region
var path = "";
if(!LOADING && !APPENDING && !CLONING) {
path = get_save_filename(@"Portable Network Graphics (.png)|*.png|
Joint Photographic Experts Group (.jpg)|*.jpg|
Graphics Interchange Format (.gif)|*.gif|
Animated WebP (.webp)|*.webp|
MPEG-4 (.mp4)|*.mp4",
"export");
key_release();
}
var node = new Node_Export(_x, _y, _group);
node.inputs[| 1].setValue(path);
node.extensionCheck();
//ds_list_add(PANEL_GRAPH.nodes_list, node);
return node;
}
function exportAll() {
if(IS_RENDERING) return;
var key = ds_map_find_first(PROJECT.nodeMap);
repeat(ds_map_size(PROJECT.nodeMap)) {
var node = PROJECT.nodeMap[? key];
key = ds_map_find_next(PROJECT.nodeMap, key);
if(!node.active) continue;
if(!is_instanceof(node, Node_Export)) continue;
node.doInspectorAction();
}
} #endregion
enum NODE_EXPORT_FORMAT {
single,
sequence,
animation,
}
function Node_Export(_x, _y, _group = noone) : Node(_x, _y, _group) constructor {
name = "Export";
preview_channel = 1;
autoUpdatedTrigger = false;
playing = false;
played = 0;
_format_still = { filter: "Portable Network Graphics (.png)|*.png|Joint Photographic Experts Group (.jpg)|*.jpg" };
_format_anim = { filter: "Graphics Interchange Format (.gif)|*.gif|Animated WebP (.webp)|*.webp" };
inputs[| 0] = nodeValue("Surface", self, JUNCTION_CONNECT.input, VALUE_TYPE.surface, noone);
inputs[| 1] = nodeValue("Paths", self, JUNCTION_CONNECT.input, VALUE_TYPE.path, "")
.setDisplay(VALUE_DISPLAY.path_save, _format_still)
.setVisible(true);
inputs[| 2] = nodeValue("Template", self, JUNCTION_CONNECT.input, VALUE_TYPE.text, "%d%n")
.rejectArray();
inputs[| 2].editWidget.format = TEXT_AREA_FORMAT.path_template;
inputs[| 2].editWidget.auto_update = true;
format_single = ["Single image", "Image sequence", "Animation"];
format_array = ["Multiple images", "Image sequences", "Animation"];
inputs[| 3] = nodeValue("Type", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, 0)
.setDisplay(VALUE_DISPLAY.enum_scroll, { data: format_single, update_hover: false })
.rejectArray();
inputs[| 4] = nodeValue("Template guides", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, 0)
.setDisplay(VALUE_DISPLAY.label,
@"%d Directory
%1d Goes up 1 level
%n File name
%f Frame
%i Array index" );
inputs[| 5] = nodeValue("Loop", self, JUNCTION_CONNECT.input, VALUE_TYPE.boolean, true)
.setVisible(false)
.rejectArray();
inputs[| 6] = nodeValue("Frame optimization", self, JUNCTION_CONNECT.input, VALUE_TYPE.boolean, false)
.setVisible(false)
.rejectArray();
inputs[| 7] = nodeValue("Color merge", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0.02)
.setDisplay(VALUE_DISPLAY.slider)
.setVisible(false)
.rejectArray();
inputs[| 8] = nodeValue("Framerate", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, 30)
.rejectArray();
format_image = [ ".png", ".jpg", ".webp" ];
format_animation = [ ".gif", ".apng", ".webp", ".mp4" ];
inputs[| 9] = nodeValue("Format", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, 0)
.setDisplay(VALUE_DISPLAY.enum_scroll, { data: format_image, update_hover: false })
.rejectArray();
inputs[| 10] = nodeValue("Quality", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 23)
.setDisplay(VALUE_DISPLAY.slider, { range: [ 0, 100, 0.1 ] })
.rejectArray();
inputs[| 11] = nodeValue("Sequence begin", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, 0);
inputs[| 12] = nodeValue("Frame range", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, [0, -1])
.setDisplay(VALUE_DISPLAY.slider_range, { range: [0, TOTAL_FRAMES, 0.1] });
png_format = [ "INDEX4", "INDEX8", "Default (PNG32)" ];
inputs[| 13] = nodeValue("Subformat", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, 2)
.setDisplay(VALUE_DISPLAY.enum_scroll, { data: png_format, update_hover: false });
inputs[| 14] = nodeValue("Frame step", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, 1);
inputs[| 15] = nodeValue("Custom Range", self, JUNCTION_CONNECT.input, VALUE_TYPE.boolean, false)
.rejectArray();
outputs[| 0] = nodeValue("Loop exit", self, JUNCTION_CONNECT.output, VALUE_TYPE.any, 0);
outputs[| 1] = nodeValue("Preview", self, JUNCTION_CONNECT.output, VALUE_TYPE.surface, noone)
.setVisible(false);
template_guide = [
["%d", "Directory"],
["%1d", "Goes up 1 level"],
["%n", "File name"],
["%f", "Frame"],
["%i", "Array index"],
];
export_template = new Inspector_Custom_Renderer(function(_x, _y, _w, _m, _hover, _focus) { #region
var _tx = _x + ui(10);
var _ty = _y;
var _tw = _w - ui(8);
var rawpath = getInputData(1);
if(is_array(rawpath)) rawpath = array_safe_get_fast(rawpath, 0, "");
var _ext = getInputData(9);
var path = pathString(rawpath);
var pathA = pathString(rawpath,, true);
path = string_replace(path, ".png", array_safe_get_fast(inputs[| 9].display_data.data, _ext, ""));
draw_set_text(f_p1, fa_left, fa_top, COLORS._main_text);
var _th = ui(12) + string_height_ext(path, -1, _tw - ui(16), true);
draw_sprite_stretched_ext(THEME.ui_panel_bg, 1, _tx, _ty, _tw, _th, COLORS.node_composite_bg_blend, 1);
var lw = 0;
var lx = _tx + ui(8);
var ly = _ty + ui(6);
draw_set_alpha(0.9);
for( var i = 0, n = array_length(pathA); i < n; i++ ) {
var _txt = pathA[i];
if(is_array(_txt)) {
switch(_txt[0]) {
case "d" : draw_set_color(COLORS.widget_text_dec_d); break;
case "n" : draw_set_color(COLORS.widget_text_dec_n); break;
case "f" : draw_set_color(COLORS.widget_text_dec_f); break;
case "i" : draw_set_color(COLORS.widget_text_dec_i); break;
case "ext" : draw_set_color(COLORS._main_text_sub); break;
}
_txt = _txt[1];
} else
draw_set_color(COLORS._main_text);
for( var j = 1; j <= string_length(_txt); j++ ) {
var ch = string_char_at(_txt, j);
var ww = string_width(ch);
if(lw + ww > _tw - ui(16)) {
lw = 0;
lx = _tx + ui(8);
ly += string_height("M");
}
draw_text(lx, ly, ch);
lw += ww;
lx += ww;
}
}
draw_set_alpha(1);
var hh = _th + ui(116);
var _cy = _y + _th + ui(8);
for( var i = 0, n = array_length(template_guide); i < n; i++ ) {
var _yy = _cy + ui(20) * i;
draw_set_text(f_p1, fa_left, fa_top, COLORS._main_text_sub);
draw_text_add(_x + ui(16 + 16), _yy, template_guide[i][0]);
draw_set_text(f_p1, fa_right, fa_top, COLORS._main_text_sub);
draw_text_add(_x + _w - ui(4 + 16), _yy, template_guide[i][1]);
}
return hh;
}); #endregion
input_display_list = [
["Export", false], 0, 1, 2, export_template,
["Format", false], 3, 9, 6, 7, 10, 13,
["Custom Range", true, 15], 12,
["Animation", false], 8, 5, 11, 14,
];
render_process_id = 0;
render_type = "";
render_target = "";
directory = TEMPDIR + string(irandom_range(100000, 999999));
converter = filepath_resolve(PREFERENCES.ImageMagick_path) + "convert.exe";
magick = filepath_resolve(PREFERENCES.ImageMagick_path) + "magick.exe";
webp = filepath_resolve(PREFERENCES.webp_path) + "webpmux.exe";
gifski = filepath_resolve(PREFERENCES.gifski_path) + "win/gifski.exe";
ffmpeg = filepath_resolve(PREFERENCES.ffmpeg_path) + "bin/ffmpeg.exe";
if(OS == os_windows) {
if(!file_exists_empty(converter) || !file_exists_empty(magick)) noti_warning($"No ImageMagick detected at {magick}, please make sure the installation is complete and ImageMagick path is set properly in preference.");
if(!file_exists_empty(webp)) noti_warning($"No webp detected at {webp}, please make sure the installation is complete and webp path is set properly in preference.");
if(!file_exists_empty(gifski)) noti_warning($"No gifski detected at {gifski}, please make sure the installation is complete and gifski path is set properly in preference.");
if(!file_exists_empty(ffmpeg)) noti_warning($"No FFmpeg detected at {ffmpeg}, please make sure the installation is complete and FFmpeg path is set properly in preference.");
} else if(OS == os_macosx) {
var check_convert = ExecutedProcessReadFromStandardOutput(shell_execute("convert", ""));
if(string_pos(check_convert, "not found")) noti_warning($"No ImageMagick installed, please install imagemagick with homebrew or use the provided 'mac-libraries-installer.command'.");
var check_webp = ExecutedProcessReadFromStandardOutput(shell_execute("webp", ""));
if(string_pos(check_webp, "not found")) noti_warning($"No webp installed, please install webp with homwbrew or use the provided 'mac-libraries-installer.command'.");
var check_ffmpeg = ExecutedProcessReadFromStandardOutput(shell_execute("ffmpeg", ""));
if(string_pos(check_ffmpeg, "not found")) noti_warning($"No FFmpeg installed, please install FFmpeg with homebrew or use the provided 'mac-libraries-installer.command'.");
var _opt = "/opt/homebrew/bin/";
converter = _opt + "convert";
magick = _opt + "magick";
webp = _opt + "webp";
ffmpeg = _opt + "ffmpeg";
}
static onValueUpdate = function(_index) { #region
var form = getInputData(3);
if(_index == 3) {
if(NOT_LOAD) inputs[| 9].setValue(0);
switch(form) {
case 0 :
case 1 :
inputs[| 1].display_data = _format_still;
break;
case 2 :
inputs[| 1].display_data = _format_anim;
break;
}
}
if(NOT_LOAD && _index == 3 && form == 1)
inputs[| 2].setValue("%d%n%3f%i");
if(NOT_LOAD && _index == 1) {
var _path = getInputData(1);
var _ext = filename_ext(_path);
switch(_ext) {
case ".png" : inputs[| 9].setValue(0); break;
case ".jpg" : inputs[| 9].setValue(1); break;
case ".gif" : inputs[| 9].setValue(0); break;
case ".webp" : inputs[| 9].setValue(1); break;
}
}
} #endregion
static extensionCheck = function() { #region
var _path = getInputData(1);
var _ext = filename_ext(_path);
switch(_ext) {
case ".png" :
inputs[| 3].setValue(0);
inputs[| 9].setValue(0);
break;
case ".jpg" :
inputs[| 3].setValue(0);
inputs[| 9].setValue(1);
break;
case ".gif" :
inputs[| 3].setValue(2);
inputs[| 9].setValue(0);
break;
case ".webp" :
inputs[| 3].setValue(2);
inputs[| 9].setValue(1);
break;
}
} #endregion
static renderWebp = function(temp_path, target_path) { #region
var _path = file_find_first(temp_path + "*.png", 0);
var frames = [];
while(_path != "") {
var _frame = string_quote(temp_path + string_replace_all(_path, ".png", "") + ".webp");
var _pathTemp = string_quote(temp_path + _path);
var shell_cmd = _pathTemp + " -define webp:lossless=true " + _frame;
array_push(frames, _frame);
shell_execute_async(magick, shell_cmd, self, false);
_path = file_find_next();
}
var rate = getInputData(8);
if(rate == 0) rate = 1;
var framerate = round(1 / rate * 1000);
var cmd = "";
for( var i = 0, n = array_length(frames); i < n; i++ )
cmd += $"-frame {frames[i]} +{framerate}+0+0+1 ";
cmd += "-bgcolor 0,0,0,0 ";
cmd += "-o " + string_quote(target_path);
render_process_id = shell_execute_async(webp, cmd, self);
render_type = "webp";
render_target = target_path;
} #endregion
static renderGif = function(temp_path, target_path) { #region
var loop = getInputData( 5);
var opti = getInputData( 6);
var fuzz = getInputData( 7);
var rate = getInputData( 8);
var qual = getInputData(10);
if(rate == 0) rate = 1;
temp_path = string_replace_all(temp_path, "/", "\\");
target_path = string_replace_all(target_path, "/", "\\");
var framerate = 100 / rate;
var loop_str = loop? 0 : 1;
var use_gifski = false;
var shell_cmd = $"-delay {framerate} -alpha set -dispose 2 -loop {loop_str}";
if(opti) shell_cmd += $" -fuzz {fuzz * 100}% -layers OptimizeFrame -layers OptimizeTransparency";
shell_cmd += $" {string_quote(temp_path)} {string_quote(target_path)}";
render_process_id = shell_execute_async(converter, shell_cmd, self);
render_type = "gif";
render_target = target_path;
} #endregion
static renderMp4 = function(temp_path, target_path) { #region
var rate = getInputData( 8);
var qual = getInputData(10); qual = clamp(qual, 0, 51);
if(rate == 0) rate = 1;
if(file_exists_empty(target_path)) file_delete(target_path);
temp_path = string_replace_all(temp_path, "/", "\\");
target_path = string_replace_all(target_path, "/", "\\");
var shell_cmd = $"-hide_banner -loglevel quiet -framerate {rate} -i \"{temp_path}%05d.png\" -c:v libx264 -r {rate} -pix_fmt yuv420p -crf {qual} {string_quote(target_path)}";
render_process_id = shell_execute_async(ffmpeg, shell_cmd, self);
render_type = "mp4";
render_target = target_path;
} #endregion
static renderApng = function(temp_path, target_path) { #region
var rate = getInputData( 8);
if(rate == 0) rate = 1;
if(file_exists_empty(target_path)) file_delete(target_path);
temp_path = string_replace_all(temp_path, "/", "\\");
target_path = string_replace_all(target_path, "/", "\\");
var shell_cmd = $"-hide_banner -loglevel quiet -framerate {rate} -i \"{temp_path}%05d.png\" -plays 0 {string_quote(target_path)}";
render_process_id = shell_execute_async(ffmpeg, shell_cmd, self);
render_type = "apng";
render_target = target_path;
} #endregion
static pathString = function(path, index = 0, _array = false) { #region
var suff = getInputData( 2);
var form = getInputData( 3);
var strt = getInputData(11);
path = string_replace_all(path, "\\", "/");
var s = _array? [] : "";
var i = 1;
var ch, ch_s;
var len = string_length(suff);
while(i <= len) {
ch = string_char_at(suff, i);
if(ch == "%") {
i++;
var res = false, str = "";
do {
ch_s = string_char_at(suff, i);
switch(ch_s) {
case "f" :
var _txt = "";
var float_str = string_digits(str);
if(float_str != "") {
var float_val = string_digits(float_str);
var str_val = max(float_val - string_length(string(CURRENT_FRAME + 1 + strt)), 0);
repeat(str_val)
_txt += "0";
}
_txt += string(CURRENT_FRAME + 1 + strt);
if(_array) array_push(s, [ "f", _txt ]);
else s += _txt;
res = true;
break;
case "i" :
var _txt = "";
var float_str = string_digits(str);
if(float_str != "") {
var float_val = string_digits(float_str);
var str_val = max(float_val - string_length(string(index)), 0);
repeat(str_val)
_txt += "0";
}
_txt += string(index);
if(_array) array_push(s, [ "i", _txt ]);
else s += _txt;
res = true;
break;
case "d" :
var dir = filename_dir(path) + "/";
var _txt = "";
var float_str = string_digits(str);
if(float_str != "") {
var float_val = toNumber(string_digits(float_str)) + 1;
var dir_s = "";
var sep = string_splice(dir, "/");
for(var j = 0; j < array_length(sep) - float_val; j++)
dir_s += sep[j] + "/";
_txt += dir_s;
} else
_txt += dir;
if(_array) array_push(s, [ "d", _txt ]);
else s += _txt;
res = true;
break;
case "n" :
var ext = filename_ext(path);
var _txt = string_replace(filename_name(path), ext, "");
if(_array) array_push(s, [ "n", _txt ]);
else s += _txt;
res = true;
break;
default :
str += ch_s;
}
i++;
} until(i > string_length(suff) || res);
} else {
if(_array) array_push(s, ch);
else s += ch;
i++;
}
}
var _e = getInputData(9);
var _ext = array_safe_get_fast(inputs[| 9].display_data.data, _e, ".png");
if(_array) array_push(s, ["ext", _ext]);
else s += _ext;
return s;
} #endregion
static save_surface = function(_surf, _path) { #region
var form = getInputData(3);
//print($">>>>>>>>>>>>>>>>>>>> save surface {_surf} - {_path} <<<<<<<<<<<<<<<<<<<<");
if(form == NODE_EXPORT_FORMAT.animation) {
surface_save_safe(_surf, _path);
return _path;
}
var extd = getInputData( 9);
var qual = getInputData(10);
var indx = getInputData(13);
var ext = array_safe_get_fast(format_image, extd, ".png");
var _pathOut = _path;
var _pathTemp = $"{directory}/{irandom_range(10000, 99999)}.png";
switch(ext) {
case ".png":
switch(indx) {
case 0 :
surface_save_safe(_surf, _pathTemp);
var shell_cmd = $"convert {string_quote(_pathTemp)} {string_quote(_pathOut)}";
shell_execute_async(magick, shell_cmd, self);
break;
case 1 :
surface_save_safe(_surf, _pathTemp);
var shell_cmd = $"convert {string_quote(_pathTemp)} PNG8:{string_quote(_pathOut)}";
shell_execute_async(magick, shell_cmd, self);
break;
case 2 :
surface_save_safe(_surf, _pathOut);
break;
}
break;
case ".jpg":
surface_save_safe(_surf, _pathTemp);
_pathOut = $"\"{string_replace_all(_path, ".png", "")}.jpg\"";
var shell_cmd = $"{string_quote(_pathTemp)} -quality {qual} {string_quote(_pathOut)}";
shell_execute_async(magick, shell_cmd, self);
break;
case ".webp":
surface_save_safe(_surf, _pathTemp);
_pathOut = $"\"{string_replace_all(_path, ".png", "")}.webp\"";
var shell_cmd = $"{string_quote(_pathTemp)} -quality {qual} -define webp:lossless=true {string_quote(_pathOut)}";
shell_execute_async(magick, shell_cmd, self);
break;
}
return _pathOut;
} #endregion
static export = function() { #region
//print($">>>>>>>>>>>>>>>>>>>> export {CURRENT_FRAME} <<<<<<<<<<<<<<<<<<<<");
var surf = getInputData( 0);
var path = getInputData( 1);
var suff = getInputData( 2);
var form = getInputData( 3);
var rang = getInputData(12);
var stps = getInputData(14);
var user = getInputData(15);
if(form >= 1 && user) {
var rng_s = rang[0];
var rng_e = rang[1];
var rng_st = stps >= 1? (CURRENT_FRAME - rng_s) % stps : 0;
if(CURRENT_FRAME < rng_s - 1) return;
if(CURRENT_FRAME > rng_e - 1) return;
if(rng_st != 0) return;
}
if(is_array(surf)) {
var p = "";
for(var i = 0; i < array_length(surf); i++) {
var _surf = surf[i];
if(!is_surface(_surf)) continue;
if(form == NODE_EXPORT_FORMAT.animation) {
p = $"{directory}/{i}/{string_lead_zero(CURRENT_FRAME, 5)}.png";
} else {
if(is_array(path) && array_length(path) == array_length(surf))
p = pathString(array_safe_get_fast(path, i), i);
else
p = pathString(path, i);
CLI_EXPORT_AMOUNT++;
}
p = save_surface(_surf, p);
}
if(form != NODE_EXPORT_FORMAT.animation && !IS_CMD) {
var noti = log_message("EXPORT", $"Export {array_length(surf)} images complete.", THEME.noti_icon_tick, COLORS._main_value_positive, false);
noti.path = filename_dir(p);
noti.setOnClick(function() { shellOpenExplorer(self.path); }, "Open in explorer", THEME.explorer);
PANEL_MENU.setNotiIcon(THEME.noti_icon_tick);
}
} else if(is_surface(surf)) {
var p = path;
if(is_array(path)) p = path[0];
if(form == NODE_EXPORT_FORMAT.animation) {
p = $"{directory}/{string_lead_zero(CURRENT_FRAME, 5)}.png";
} else {
p = pathString(p);
CLI_EXPORT_AMOUNT++;
}
p = save_surface(surf, p);
if(form != NODE_EXPORT_FORMAT.animation && !IS_CMD) {
var noti = log_message("EXPORT", $"Export image as {p}", THEME.noti_icon_tick, COLORS._main_value_positive, false);
noti.path = filename_dir(p);
noti.setOnClick(function() { shellOpenExplorer(self.path); }, "Open in explorer", THEME.explorer);
PANEL_MENU.setNotiIcon(THEME.noti_icon_tick);
}
}
} #endregion
static renderCompleted = function() { #region
var surf = getInputData( 0);
var path = getInputData( 1);
var suff = getInputData( 2);
var extd = getInputData( 9);
var temp_path, target_path;
update_on_frame = false;
if(is_array(surf)) {
for(var i = 0; i < array_length(surf); i++) {
temp_path = $"{directory}/{i}/*.png";
if(is_array(path)) target_path = pathString(array_safe_get_fast(path, i), i);
else target_path = pathString(path, i);
switch(format_animation[extd]) {
case ".gif" :
target_path = string_replace(target_path, ".png", ".gif");
renderGif(temp_path, target_path);
break;
case ".webp" :
target_path = string_replace(target_path, ".png", ".webp");
renderWebp(temp_path, target_path);
break;
case ".mp4" :
target_path = string_replace(target_path, ".png", ".mp4");
renderMp4(temp_path, target_path);
break;
case ".apng" :
target_path = string_replace(target_path, ".png", ".apng");
renderApng(temp_path, target_path);
break;
}
}
} else {
target_path = pathString(path);
switch(format_animation[extd]) {
case ".gif" :
target_path = string_replace(target_path, ".png", ".gif");
renderGif(directory + "/*.png", target_path);
break;
case ".webp" :
target_path = string_replace(target_path, ".png", ".webp");
renderWebp(directory + "/", target_path);
break;
case ".mp4" :
target_path = string_replace(target_path, ".png", ".mp4");
renderMp4(directory + "/", target_path);
break;
case ".apng" :
target_path = string_replace(target_path, ".png", ".apng");
renderApng(directory + "/", target_path);
break;
}
}
updatedOutTrigger.setValue(true);
CLI_EXPORT_AMOUNT++;
} #endregion
insp1UpdateTooltip = "Export";
insp1UpdateIcon = [ THEME.sequence_control, 1, COLORS._main_value_positive ];
insp2UpdateTooltip = "Export All";
insp2UpdateIcon = [ THEME.play_all, 0, COLORS._main_value_positive ];
static onInspector1Update = function() { #region
if(IS_RENDERING) return;
if(isInLoop()) RENDER_ALL
else doInspectorAction();
} #endregion
static onInspector2Update = function() { #region
if(IS_RENDERING) return;
exportAll();
} #endregion
static doInspectorAction = function() { #region
if(!IS_CMD && (LOADING || APPENDING)) return;
var path = getInputData(1);
if(path == "") return;
var form = getInputData(3);
if(form == NODE_EXPORT_FORMAT.single) {
Render();
export();
updatedOutTrigger.setValue(true);
return;
}
update_on_frame = true;
playing = true;
played = 0;
PROJECT.animator.render();
if(IS_CMD) array_push(PROGRAM_ARGUMENTS._exporting, node_id);
if(directory_exists(directory))
directory_destroy(directory);
directory_create(directory);
} #endregion
static step = function() { #region
insp1UpdateActive = !IS_RENDERING;
insp2UpdateActive = !IS_RENDERING;
var surf = getInputData( 0);
var pngf = getInputData(13);
if(is_array(surf)) {
inputs[| 3].display_data.data = format_array;
inputs[| 3].editWidget.data_list = format_array;
} else {
inputs[| 3].display_data.data = format_single;
inputs[| 3].editWidget.data_list = format_single;
}
outputs[| 1].setValue(surf);
var anim = getInputData(3); // single, sequence, animation
var extn = getInputData(9);
var user = getInputData(15);
inputs[| 11].setVisible(anim == 1);
inputs[| 12].editWidget.minn = FIRST_FRAME + 1;
inputs[| 12].editWidget.maxx = LAST_FRAME + 1;
if(!user) inputs[| 12].setValueDirect([ FIRST_FRAME + 1, LAST_FRAME + 1], noone, false, 0, false);
inputs[| 14].setVisible(anim > 0);
if(anim == NODE_EXPORT_FORMAT.animation) {
var _fmt = array_safe_get_fast(format_animation, extn);
inputs[| 5].setVisible(_fmt == ".gif");
inputs[| 6].setVisible(_fmt == ".gif");
inputs[| 7].setVisible(_fmt == ".gif");
inputs[| 8].setVisible(true);
inputs[| 9].display_data.data = format_animation;
inputs[| 9].editWidget.data_list = format_animation;
inputs[| 13].setVisible(false);
if(_fmt == ".mp4") {
inputs[| 10].setName("CRF value");
inputs[| 10].tooltip = "Quality of the output, with 0 being the highest (and largest file size), and 51 being the lowest.";
inputs[| 10].setVisible(true);
inputs[| 10].editWidget.minn = 0;
inputs[| 10].editWidget.maxx = 51;
} else
inputs[| 10].setVisible(false);
} else {
var _fmt = array_safe_get_fast(format_image, extn);
inputs[| 5].setVisible(false);
inputs[| 6].setVisible(false);
inputs[| 7].setVisible(false);
inputs[| 8].setVisible(false);
inputs[| 9].display_data.data = format_image;
inputs[| 9].editWidget.data_list = format_image;
inputs[| 13].setVisible(_fmt == ".png");
if(_fmt == ".jpg" || _fmt == ".webp") {
inputs[| 10].setName("Quality");
inputs[| 10].tooltip = "Quality of the output.";
inputs[| 10].setVisible(true);
inputs[| 10].editWidget.minn = 0;
inputs[| 10].editWidget.maxx = 100;
} else
inputs[| 10].setVisible(false);
}
outputs[| 0].visible = isInLoop();
if(render_process_id != 0) {
var res = ProcIdExists(render_process_id);
if(res == 0 || OS == os_macosx) {
if(!IS_CMD) {
var noti = log_message("EXPORT", $"Export {render_type} as {render_target}", THEME.noti_icon_tick, COLORS._main_value_positive, false);
noti.path = filename_dir(render_target);
noti.setOnClick(function() { shellOpenExplorer(self.path); }, "Open in explorer", THEME.explorer);
PANEL_MENU.setNotiIcon(THEME.noti_icon_tick);
}
render_process_id = 0;
if(IS_CMD) array_remove(PROGRAM_ARGUMENTS._exporting, node_id);
}
}
} #endregion
static update = function(frame = CURRENT_FRAME) { #region
var anim = getInputData(3);
if(anim == NODE_EXPORT_FORMAT.single) {
if(isInLoop()) export();
return;
}
if(!PROJECT.animator.is_playing) {
playing = false;
return;
}
if(!playing) return;
export();
if(IS_LAST_FRAME && anim == NODE_EXPORT_FORMAT.animation)
renderCompleted();
} #endregion
static onDrawNode = function(xx, yy, _mx, _my, _s, _hover, _focus) { #region
graph_preview_alpha = 1;
if(render_process_id != 0) {
graph_preview_alpha = 0.5;
draw_sprite_ui(THEME.loading, 0, xx + w * _s / 2, yy + h * _s / 2, _s, _s, current_time / 2, COLORS._main_icon, 1);
}
} #endregion
static doApplyDeserialize = function() { onValueUpdate(3); }
}

View file

@ -0,0 +1,857 @@
// 2024-05-01 11:49:13
function Node_create_Export(_x, _y, _group = noone) { #region
var path = "";
if(!LOADING && !APPENDING && !CLONING) {
path = get_save_filename(@"Portable Network Graphics (.png)|*.png|
Joint Photographic Experts Group (.jpg)|*.jpg|
Graphics Interchange Format (.gif)|*.gif|
Animated WebP (.webp)|*.webp|
MPEG-4 (.mp4)|*.mp4",
"export");
key_release();
}
var node = new Node_Export(_x, _y, _group);
node.inputs[| 1].setValue(path);
node.extensionCheck();
//ds_list_add(PANEL_GRAPH.nodes_list, node);
return node;
}
function exportAll() {
if(IS_RENDERING) return;
var key = ds_map_find_first(PROJECT.nodeMap);
repeat(ds_map_size(PROJECT.nodeMap)) {
var node = PROJECT.nodeMap[? key];
key = ds_map_find_next(PROJECT.nodeMap, key);
if(!node.active) continue;
if(!is_instanceof(node, Node_Export)) continue;
node.doInspectorAction();
}
} #endregion
enum NODE_EXPORT_FORMAT {
single,
sequence,
animation,
}
function Node_Export(_x, _y, _group = noone) : Node(_x, _y, _group) constructor {
name = "Export";
preview_channel = 1;
autoUpdatedTrigger = false;
playing = false;
played = 0;
_format_still = { filter: "Portable Network Graphics (.png)|*.png|Joint Photographic Experts Group (.jpg)|*.jpg" };
_format_anim = { filter: "Graphics Interchange Format (.gif)|*.gif|Animated WebP (.webp)|*.webp" };
inputs[| 0] = nodeValue("Surface", self, JUNCTION_CONNECT.input, VALUE_TYPE.surface, noone);
inputs[| 1] = nodeValue("Paths", self, JUNCTION_CONNECT.input, VALUE_TYPE.path, "")
.setDisplay(VALUE_DISPLAY.path_save, _format_still)
.setVisible(true);
inputs[| 2] = nodeValue("Template", self, JUNCTION_CONNECT.input, VALUE_TYPE.text, "%d%n")
.rejectArray();
inputs[| 2].editWidget.format = TEXT_AREA_FORMAT.path_template;
inputs[| 2].editWidget.auto_update = true;
format_single = ["Single image", "Image sequence", "Animation"];
format_array = ["Multiple images", "Image sequences", "Animation"];
inputs[| 3] = nodeValue("Type", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, 0)
.setDisplay(VALUE_DISPLAY.enum_scroll, { data: format_single, update_hover: false })
.rejectArray();
inputs[| 4] = nodeValue("Template guides", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, 0)
.setDisplay(VALUE_DISPLAY.label,
@"%d Directory
%1d Goes up 1 level
%n File name
%f Frame
%i Array index" );
inputs[| 5] = nodeValue("Loop", self, JUNCTION_CONNECT.input, VALUE_TYPE.boolean, true)
.setVisible(false)
.rejectArray();
inputs[| 6] = nodeValue("Frame optimization", self, JUNCTION_CONNECT.input, VALUE_TYPE.boolean, false)
.setVisible(false)
.rejectArray();
inputs[| 7] = nodeValue("Color merge", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0.02)
.setDisplay(VALUE_DISPLAY.slider)
.setVisible(false)
.rejectArray();
inputs[| 8] = nodeValue("Framerate", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, 30)
.rejectArray();
format_image = [ ".png", ".jpg", ".webp" ];
format_animation = [ ".gif", ".apng", ".webp", ".mp4" ];
inputs[| 9] = nodeValue("Format", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, 0)
.setDisplay(VALUE_DISPLAY.enum_scroll, { data: format_image, update_hover: false })
.rejectArray();
inputs[| 10] = nodeValue("Quality", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 23)
.setDisplay(VALUE_DISPLAY.slider, { range: [ 0, 100, 0.1 ] })
.rejectArray();
inputs[| 11] = nodeValue("Sequence begin", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, 0);
inputs[| 12] = nodeValue("Frame range", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, [0, -1])
.setDisplay(VALUE_DISPLAY.slider_range, { range: [0, TOTAL_FRAMES, 0.1] });
png_format = [ "INDEX4", "INDEX8", "Default (PNG32)" ];
inputs[| 13] = nodeValue("Subformat", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, 2)
.setDisplay(VALUE_DISPLAY.enum_scroll, { data: png_format, update_hover: false });
inputs[| 14] = nodeValue("Frame step", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, 1);
inputs[| 15] = nodeValue("Custom Range", self, JUNCTION_CONNECT.input, VALUE_TYPE.boolean, false)
.rejectArray();
outputs[| 0] = nodeValue("Loop exit", self, JUNCTION_CONNECT.output, VALUE_TYPE.any, 0);
outputs[| 1] = nodeValue("Preview", self, JUNCTION_CONNECT.output, VALUE_TYPE.surface, noone)
.setVisible(false);
template_guide = [
["%d", "Directory"],
["%1d", "Goes up 1 level"],
["%n", "File name"],
["%f", "Frame"],
["%i", "Array index"],
];
export_template = new Inspector_Custom_Renderer(function(_x, _y, _w, _m, _hover, _focus) { #region
var _tx = _x + ui(10);
var _ty = _y;
var _tw = _w - ui(8);
var rawpath = getInputData(1);
if(is_array(rawpath)) rawpath = array_safe_get_fast(rawpath, 0, "");
var _ext = getInputData(9);
var path = pathString(rawpath);
var pathA = pathString(rawpath,, true);
path = string_replace(path, ".png", array_safe_get_fast(inputs[| 9].display_data.data, _ext, ""));
draw_set_text(f_p1, fa_left, fa_top, COLORS._main_text);
var _th = ui(12) + string_height_ext(path, -1, _tw - ui(16), true);
draw_sprite_stretched_ext(THEME.ui_panel_bg, 1, _tx, _ty, _tw, _th, COLORS.node_composite_bg_blend, 1);
var lw = 0;
var lx = _tx + ui(8);
var ly = _ty + ui(6);
draw_set_alpha(0.9);
for( var i = 0, n = array_length(pathA); i < n; i++ ) {
var _txt = pathA[i];
if(is_array(_txt)) {
switch(_txt[0]) {
case "d" : draw_set_color(COLORS.widget_text_dec_d); break;
case "n" : draw_set_color(COLORS.widget_text_dec_n); break;
case "f" : draw_set_color(COLORS.widget_text_dec_f); break;
case "i" : draw_set_color(COLORS.widget_text_dec_i); break;
case "ext" : draw_set_color(COLORS._main_text_sub); break;
}
_txt = _txt[1];
} else
draw_set_color(COLORS._main_text);
for( var j = 1; j <= string_length(_txt); j++ ) {
var ch = string_char_at(_txt, j);
var ww = string_width(ch);
if(lw + ww > _tw - ui(16)) {
lw = 0;
lx = _tx + ui(8);
ly += string_height("M");
}
draw_text(lx, ly, ch);
lw += ww;
lx += ww;
}
}
draw_set_alpha(1);
var hh = _th + ui(116);
var _cy = _y + _th + ui(8);
for( var i = 0, n = array_length(template_guide); i < n; i++ ) {
var _yy = _cy + ui(20) * i;
draw_set_text(f_p1, fa_left, fa_top, COLORS._main_text_sub);
draw_text_add(_x + ui(16 + 16), _yy, template_guide[i][0]);
draw_set_text(f_p1, fa_right, fa_top, COLORS._main_text_sub);
draw_text_add(_x + _w - ui(4 + 16), _yy, template_guide[i][1]);
}
return hh;
}); #endregion
input_display_list = [
["Export", false], 0, 1, 2, export_template,
["Format", false], 3, 9, 6, 7, 10, 13,
["Custom Range", true, 15], 12,
["Animation", false], 8, 5, 11, 14,
];
render_process_id = 0;
render_type = "";
render_target = "";
directory = TEMPDIR + string(irandom_range(100000, 999999));
converter = filepath_resolve(PREFERENCES.ImageMagick_path) + "convert.exe";
magick = filepath_resolve(PREFERENCES.ImageMagick_path) + "magick.exe";
webp = filepath_resolve(PREFERENCES.webp_path) + "webpmux.exe";
gifski = filepath_resolve(PREFERENCES.gifski_path) + "win/gifski.exe";
ffmpeg = filepath_resolve(PREFERENCES.ffmpeg_path) + "bin/ffmpeg.exe";
if(OS == os_windows) {
if(!file_exists_empty(converter) || !file_exists_empty(magick)) noti_warning($"No ImageMagick detected at {magick}, please make sure the installation is complete and ImageMagick path is set properly in preference.");
if(!file_exists_empty(webp)) noti_warning($"No webp detected at {webp}, please make sure the installation is complete and webp path is set properly in preference.");
if(!file_exists_empty(gifski)) noti_warning($"No gifski detected at {gifski}, please make sure the installation is complete and gifski path is set properly in preference.");
if(!file_exists_empty(ffmpeg)) noti_warning($"No FFmpeg detected at {ffmpeg}, please make sure the installation is complete and FFmpeg path is set properly in preference.");
} else if(OS == os_macosx) {
var check_convert = ExecutedProcessReadFromStandardOutput(shell_execute("convert", ""));
if(string_pos(check_convert, "not found")) noti_warning($"No ImageMagick installed, please install imagemagick with homebrew or use the provided 'mac-libraries-installer.command'.");
var check_webp = ExecutedProcessReadFromStandardOutput(shell_execute("webp", ""));
if(string_pos(check_webp, "not found")) noti_warning($"No webp installed, please install webp with homwbrew or use the provided 'mac-libraries-installer.command'.");
var check_ffmpeg = ExecutedProcessReadFromStandardOutput(shell_execute("ffmpeg", ""));
if(string_pos(check_ffmpeg, "not found")) noti_warning($"No FFmpeg installed, please install FFmpeg with homebrew or use the provided 'mac-libraries-installer.command'.");
var _opt = "/opt/homebrew/bin/";
converter = _opt + "convert";
magick = _opt + "magick";
webp = _opt + "webp";
ffmpeg = _opt + "ffmpeg";
}
static onValueUpdate = function(_index) { #region
var form = getInputData(3);
if(_index == 3) {
if(NOT_LOAD) inputs[| 9].setValue(0);
switch(form) {
case 0 :
case 1 :
inputs[| 1].display_data = _format_still;
break;
case 2 :
inputs[| 1].display_data = _format_anim;
break;
}
}
if(NOT_LOAD && _index == 3 && form == 1)
inputs[| 2].setValue("%d%n%3f%i");
if(NOT_LOAD && _index == 1) {
var _path = getInputData(1);
var _ext = filename_ext(_path);
switch(_ext) {
case ".png" : inputs[| 9].setValue(0); break;
case ".jpg" : inputs[| 9].setValue(1); break;
case ".gif" : inputs[| 9].setValue(0); break;
case ".webp" : inputs[| 9].setValue(1); break;
}
}
} #endregion
static extensionCheck = function() { #region
var _path = getInputData(1);
var _ext = filename_ext(_path);
switch(_ext) {
case ".png" :
inputs[| 3].setValue(0);
inputs[| 9].setValue(0);
break;
case ".jpg" :
inputs[| 3].setValue(0);
inputs[| 9].setValue(1);
break;
case ".gif" :
inputs[| 3].setValue(2);
inputs[| 9].setValue(0);
break;
case ".webp" :
inputs[| 3].setValue(2);
inputs[| 9].setValue(1);
break;
}
} #endregion
static renderWebp = function(temp_path, target_path) { #region
var _path = file_find_first(temp_path + "*.png", 0);
var frames = [];
while(_path != "") {
var _frame = string_quote(temp_path + string_replace_all(_path, ".png", "") + ".webp");
var _pathTemp = string_quote(temp_path + _path);
var shell_cmd = _pathTemp + " -define webp:lossless=true " + _frame;
array_push(frames, _frame);
shell_execute_async(magick, shell_cmd, self, false);
_path = file_find_next();
}
var rate = getInputData(8);
if(rate == 0) rate = 1;
var framerate = round(1 / rate * 1000);
var cmd = "";
for( var i = 0, n = array_length(frames); i < n; i++ )
cmd += $"-frame {frames[i]} +{framerate}+0+0+1 ";
cmd += "-bgcolor 0,0,0,0 ";
cmd += "-o " + string_quote(target_path);
render_process_id = shell_execute_async(webp, cmd, self);
render_type = "webp";
render_target = target_path;
} #endregion
static renderGif = function(temp_path, target_path) { #region
var loop = getInputData( 5);
var opti = getInputData( 6);
var fuzz = getInputData( 7);
var rate = getInputData( 8);
var qual = getInputData(10);
if(rate == 0) rate = 1;
temp_path = string_replace_all(temp_path, "/", "\\");
target_path = string_replace_all(target_path, "/", "\\");
var framerate = 100 / rate;
var loop_str = loop? 0 : 1;
var use_gifski = false;
var shell_cmd = $"-delay {framerate} -alpha set -dispose 2 -loop {loop_str}";
if(opti) shell_cmd += $" -fuzz {fuzz * 100}% -layers OptimizeFrame -layers OptimizeTransparency";
shell_cmd += $" {string_quote(temp_path)} {string_quote(target_path)}";
render_process_id = shell_execute_async(converter, shell_cmd, self);
render_type = "gif";
render_target = target_path;
} #endregion
static renderMp4 = function(temp_path, target_path) { #region
var rate = getInputData( 8);
var qual = getInputData(10); qual = clamp(qual, 0, 51);
if(rate == 0) rate = 1;
if(file_exists_empty(target_path)) file_delete(target_path);
temp_path = string_replace_all(temp_path, "/", "\\");
target_path = string_replace_all(target_path, "/", "\\");
var shell_cmd = $"-hide_banner -loglevel quiet -framerate {rate} -i \"{temp_path}%05d.png\" -c:v libx264 -r {rate} -pix_fmt yuv420p -crf {qual} {string_quote(target_path)}";
render_process_id = shell_execute_async(ffmpeg, shell_cmd, self);
render_type = "mp4";
render_target = target_path;
} #endregion
static renderApng = function(temp_path, target_path) { #region
var rate = getInputData( 8);
if(rate == 0) rate = 1;
if(file_exists_empty(target_path)) file_delete(target_path);
temp_path = string_replace_all(temp_path, "/", "\\");
target_path = string_replace_all(target_path, "/", "\\");
var shell_cmd = $"-hide_banner -loglevel quiet -framerate {rate} -i \"{temp_path}%05d.png\" -plays 0 {string_quote(target_path)}";
render_process_id = shell_execute_async(ffmpeg, shell_cmd, self);
render_type = "apng";
render_target = target_path;
} #endregion
static pathString = function(path, index = 0, _array = false) { #region
var suff = getInputData( 2);
var form = getInputData( 3);
var strt = getInputData(11);
path = string_replace_all(path, "\\", "/");
var s = _array? [] : "";
var i = 1;
var ch, ch_s;
var len = string_length(suff);
while(i <= len) {
ch = string_char_at(suff, i);
if(ch == "%") {
i++;
var res = false, str = "";
do {
ch_s = string_char_at(suff, i);
switch(ch_s) {
case "f" :
var _txt = "";
var float_str = string_digits(str);
if(float_str != "") {
var float_val = string_digits(float_str);
var str_val = max(float_val - string_length(string(CURRENT_FRAME + 1 + strt)), 0);
repeat(str_val)
_txt += "0";
}
_txt += string(CURRENT_FRAME + 1 + strt);
if(_array) array_push(s, [ "f", _txt ]);
else s += _txt;
res = true;
break;
case "i" :
var _txt = "";
var float_str = string_digits(str);
if(float_str != "") {
var float_val = string_digits(float_str);
var str_val = max(float_val - string_length(string(index)), 0);
repeat(str_val)
_txt += "0";
}
_txt += string(index);
if(_array) array_push(s, [ "i", _txt ]);
else s += _txt;
res = true;
break;
case "d" :
var dir = filename_dir(path) + "/";
var _txt = "";
var float_str = string_digits(str);
if(float_str != "") {
var float_val = toNumber(string_digits(float_str)) + 1;
var dir_s = "";
var sep = string_splice(dir, "/");
for(var j = 0; j < array_length(sep) - float_val; j++)
dir_s += sep[j] + "/";
_txt += dir_s;
} else
_txt += dir;
if(_array) array_push(s, [ "d", _txt ]);
else s += _txt;
res = true;
break;
case "n" :
var ext = filename_ext(path);
var _txt = string_replace(filename_name(path), ext, "");
if(_array) array_push(s, [ "n", _txt ]);
else s += _txt;
res = true;
break;
default :
str += ch_s;
}
i++;
} until(i > string_length(suff) || res);
} else {
if(_array) array_push(s, ch);
else s += ch;
i++;
}
}
var _e = getInputData(9);
var _ext = array_safe_get_fast(inputs[| 9].display_data.data, _e, ".png");
if(_array) array_push(s, ["ext", _ext]);
else s += _ext;
return s;
} #endregion
static save_surface = function(_surf, _path) { #region
var form = getInputData(3);
//print($">>>>>>>>>>>>>>>>>>>> save surface {_surf} - {_path} <<<<<<<<<<<<<<<<<<<<");
if(form == NODE_EXPORT_FORMAT.animation) {
surface_save_safe(_surf, _path);
return _path;
}
var extd = getInputData( 9);
var qual = getInputData(10);
var indx = getInputData(13);
var ext = array_safe_get_fast(format_image, extd, ".png");
var _pathOut = _path;
var _pathTemp = $"{directory}/{irandom_range(10000, 99999)}.png";
switch(ext) {
case ".png":
switch(indx) {
case 0 :
surface_save_safe(_surf, _pathTemp);
var shell_cmd = $"convert {string_quote(_pathTemp)} {string_quote(_pathOut)}";
shell_execute_async(magick, shell_cmd, self);
break;
case 1 :
surface_save_safe(_surf, _pathTemp);
var shell_cmd = $"convert {string_quote(_pathTemp)} PNG8:{string_quote(_pathOut)}";
shell_execute_async(magick, shell_cmd, self);
break;
case 2 :
surface_save_safe(_surf, _pathOut);
break;
}
break;
case ".jpg":
surface_save_safe(_surf, _pathTemp);
_pathOut = $"\"{string_replace_all(_path, ".png", "")}.jpg\"";
var shell_cmd = $"{string_quote(_pathTemp)} -quality {qual} {string_quote(_pathOut)}";
shell_execute_async(magick, shell_cmd, self);
break;
case ".webp":
surface_save_safe(_surf, _pathTemp);
_pathOut = $"\"{string_replace_all(_path, ".png", "")}.webp\"";
var shell_cmd = $"{string_quote(_pathTemp)} -quality {qual} -define webp:lossless=true {string_quote(_pathOut)}";
shell_execute_async(magick, shell_cmd, self);
break;
}
return _pathOut;
} #endregion
static export = function() { #region
//print($">>>>>>>>>>>>>>>>>>>> export {CURRENT_FRAME} <<<<<<<<<<<<<<<<<<<<");
var surf = getInputData( 0);
var path = getInputData( 1);
var suff = getInputData( 2);
var form = getInputData( 3);
var rang = getInputData(12);
var stps = getInputData(14);
var user = getInputData(15);
if(form >= 1 && user) {
var rng_s = rang[0];
var rng_e = rang[1];
var rng_st = stps >= 1? (CURRENT_FRAME - rng_s) % stps : 0;
if(CURRENT_FRAME < rng_s - 1) return;
if(CURRENT_FRAME > rng_e - 1) return;
if(rng_st != 0) return;
}
if(is_array(surf)) {
var p = "";
for(var i = 0; i < array_length(surf); i++) {
var _surf = surf[i];
if(!is_surface(_surf)) continue;
if(form == NODE_EXPORT_FORMAT.animation) {
p = $"{directory}/{i}/{string_lead_zero(CURRENT_FRAME, 5)}.png";
} else {
if(is_array(path) && array_length(path) == array_length(surf))
p = pathString(array_safe_get_fast(path, i), i);
else
p = pathString(path, i);
CLI_EXPORT_AMOUNT++;
}
p = save_surface(_surf, p);
}
if(form != NODE_EXPORT_FORMAT.animation && !IS_CMD) {
var noti = log_message("EXPORT", $"Export {array_length(surf)} images complete.", THEME.noti_icon_tick, COLORS._main_value_positive, false);
noti.path = filename_dir(p);
noti.setOnClick(function() { shellOpenExplorer(self.path); }, "Open in explorer", THEME.explorer);
PANEL_MENU.setNotiIcon(THEME.noti_icon_tick);
}
} else if(is_surface(surf)) {
var p = path;
if(is_array(path)) p = path[0];
if(form == NODE_EXPORT_FORMAT.animation) {
p = $"{directory}/{string_lead_zero(CURRENT_FRAME, 5)}.png";
} else {
p = pathString(p);
CLI_EXPORT_AMOUNT++;
}
p = save_surface(surf, p);
if(form != NODE_EXPORT_FORMAT.animation && !IS_CMD) {
var noti = log_message("EXPORT", $"Export image as {p}", THEME.noti_icon_tick, COLORS._main_value_positive, false);
noti.path = filename_dir(p);
noti.setOnClick(function() { shellOpenExplorer(self.path); }, "Open in explorer", THEME.explorer);
PANEL_MENU.setNotiIcon(THEME.noti_icon_tick);
}
}
} #endregion
static renderCompleted = function() { #region
var surf = getInputData( 0);
var path = getInputData( 1);
var suff = getInputData( 2);
var extd = getInputData( 9);
var temp_path, target_path;
update_on_frame = false;
if(is_array(surf)) {
for(var i = 0; i < array_length(surf); i++) {
temp_path = $"{directory}/{i}/*.png";
if(is_array(path)) target_path = pathString(array_safe_get_fast(path, i), i);
else target_path = pathString(path, i);
switch(format_animation[extd]) {
case ".gif" :
target_path = string_replace(target_path, ".png", ".gif");
renderGif(temp_path, target_path);
break;
case ".webp" :
target_path = string_replace(target_path, ".png", ".webp");
renderWebp(temp_path, target_path);
break;
case ".mp4" :
target_path = string_replace(target_path, ".png", ".mp4");
renderMp4(temp_path, target_path);
break;
case ".apng" :
target_path = string_replace(target_path, ".png", ".apng");
renderApng(temp_path, target_path);
break;
}
}
} else {
target_path = pathString(path);
switch(format_animation[extd]) {
case ".gif" :
target_path = string_replace(target_path, ".png", ".gif");
renderGif(directory + "/*.png", target_path);
break;
case ".webp" :
target_path = string_replace(target_path, ".png", ".webp");
renderWebp(directory + "/", target_path);
break;
case ".mp4" :
target_path = string_replace(target_path, ".png", ".mp4");
renderMp4(directory + "/", target_path);
break;
case ".apng" :
target_path = string_replace(target_path, ".png", ".apng");
renderApng(directory + "/", target_path);
break;
}
}
updatedOutTrigger.setValue(true);
CLI_EXPORT_AMOUNT++;
} #endregion
insp1UpdateTooltip = "Export";
insp1UpdateIcon = [ THEME.sequence_control, 1, COLORS._main_value_positive ];
insp2UpdateTooltip = "Export All";
insp2UpdateIcon = [ THEME.play_all, 0, COLORS._main_value_positive ];
static onInspector1Update = function() { #region
if(IS_RENDERING) return;
if(isInLoop()) RENDER_ALL
else doInspectorAction();
} #endregion
static onInspector2Update = function() { #region
if(IS_RENDERING) return;
exportAll();
} #endregion
static doInspectorAction = function() { #region
if(!IS_CMD && (LOADING || APPENDING)) return;
var path = getInputData(1);
if(path == "") return;
var form = getInputData(3);
if(form == NODE_EXPORT_FORMAT.single) {
Render();
export();
updatedOutTrigger.setValue(true);
return;
}
update_on_frame = true;
playing = true;
played = 0;
PROJECT.animator.render();
if(IS_CMD) array_push(PROGRAM_ARGUMENTS._exporting, node_id);
if(directory_exists(directory))
directory_destroy(directory);
directory_create(directory);
} #endregion
static step = function() { #region
insp1UpdateActive = !IS_RENDERING;
insp2UpdateActive = !IS_RENDERING;
var surf = getInputData( 0);
var pngf = getInputData(13);
if(is_array(surf)) {
inputs[| 3].display_data.data = format_array;
inputs[| 3].editWidget.data_list = format_array;
} else {
inputs[| 3].display_data.data = format_single;
inputs[| 3].editWidget.data_list = format_single;
}
outputs[| 1].setValue(surf);
var anim = getInputData(3); // single, sequence, animation
var extn = getInputData(9);
var user = getInputData(15);
inputs[| 11].setVisible(anim == 1);
inputs[| 12].editWidget.minn = FIRST_FRAME + 1;
inputs[| 12].editWidget.maxx = LAST_FRAME + 1;
if(!user) inputs[| 12].setValueDirect([ FIRST_FRAME + 1, LAST_FRAME + 1], noone, false, 0, false);
inputs[| 14].setVisible(anim > 0);
if(anim == NODE_EXPORT_FORMAT.animation) {
var _fmt = array_safe_get_fast(format_animation, extn);
inputs[| 5].setVisible(_fmt == ".gif");
inputs[| 6].setVisible(_fmt == ".gif");
inputs[| 7].setVisible(_fmt == ".gif");
inputs[| 8].setVisible(true);
inputs[| 9].display_data.data = format_animation;
inputs[| 9].editWidget.data_list = format_animation;
inputs[| 13].setVisible(false);
if(_fmt == ".mp4") {
inputs[| 10].setName("CRF value");
inputs[| 10].tooltip = "Quality of the output, with 0 being the highest (and largest file size), and 51 being the lowest.";
inputs[| 10].setVisible(true);
inputs[| 10].editWidget.minn = 0;
inputs[| 10].editWidget.maxx = 51;
} else
inputs[| 10].setVisible(false);
} else {
var _fmt = array_safe_get_fast(format_image, extn);
inputs[| 5].setVisible(false);
inputs[| 6].setVisible(false);
inputs[| 7].setVisible(false);
inputs[| 8].setVisible(false);
inputs[| 9].display_data.data = format_image;
inputs[| 9].editWidget.data_list = format_image;
inputs[| 13].setVisible(_fmt == ".png");
if(_fmt == ".jpg" || _fmt == ".webp") {
inputs[| 10].setName("Quality");
inputs[| 10].tooltip = "Quality of the output.";
inputs[| 10].setVisible(true);
inputs[| 10].editWidget.minn = 0;
inputs[| 10].editWidget.maxx = 100;
} else
inputs[| 10].setVisible(false);
}
outputs[| 0].visible = isInLoop();
if(render_process_id != 0) {
var res = ProcIdExists(render_process_id);
if(res == 0 || OS == os_macosx) {
if(!IS_CMD) {
var noti = log_message("EXPORT", $"Export {render_type} as {render_target}", THEME.noti_icon_tick, COLORS._main_value_positive, false);
noti.path = filename_dir(render_target);
noti.setOnClick(function() { shellOpenExplorer(self.path); }, "Open in explorer", THEME.explorer);
PANEL_MENU.setNotiIcon(THEME.noti_icon_tick);
}
render_process_id = 0;
if(IS_CMD) array_remove(PROGRAM_ARGUMENTS._exporting, node_id);
}
}
} #endregion
static update = function(frame = CURRENT_FRAME) { #region
var anim = getInputData(3);
if(anim == NODE_EXPORT_FORMAT.single) {
if(isInLoop()) export();
return;
}
if(!PROJECT.animator.is_playing) {
playing = false;
return;
}
if(!playing) return;
export();
if(IS_LAST_FRAME && anim == NODE_EXPORT_FORMAT.animation)
renderCompleted();
} #endregion
static onDrawNode = function(xx, yy, _mx, _my, _s, _hover, _focus) { #region
graph_preview_alpha = 1;
if(render_process_id != 0) {
graph_preview_alpha = 0.5;
draw_sprite_ui(THEME.loading, 0, xx + w * _s / 2, yy + h * _s / 2, _s, _s, current_time / 2, COLORS._main_icon, 1);
}
} #endregion
static doApplyDeserialize = function() { onValueUpdate(3); }
}

View file

@ -0,0 +1,129 @@
// 2024-05-01 09:30:41
function Node_Random_Tile(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) constructor {
name = "Random Tile";
inputs[| 0] = nodeValue("Dimension", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, DEF_SURF )
.setDisplay(VALUE_DISPLAY.vector);
inputs[| 1] = nodeValue("Position", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, [ 0, 0 ])
.setDisplay(VALUE_DISPLAY.vector)
.setUnitRef(function(index) { return getDimension(index); });
inputs[| 2] = nodeValue("Scale", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, [ 2, 2 ])
.setDisplay(VALUE_DISPLAY.vector)
.setMappable(11);
inputs[| 3] = nodeValue("Angle", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0)
.setDisplay(VALUE_DISPLAY.rotation)
.setMappable(12);
inputs[| 4] = nodeValue("Gap", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0.1)
.setDisplay(VALUE_DISPLAY.slider, { range: [0, 0.5, 0.001] })
.setMappable(13);
inputs[| 5] = nodeValue("Tile color", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white) )
.setMappable(17);
inputs[| 6] = nodeValue("Gap color", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, c_black);
inputs[| 7] = nodeValue("Render type", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, 0)
.setDisplay(VALUE_DISPLAY.enum_scroll, ["Colored tile", "Height map", "Texture grid"]);
inputs[| 8] = nodeValue("Seed", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, irandom_range(10000, 99999));
inputs[| 9] = nodeValue("Texture", self, JUNCTION_CONNECT.input, VALUE_TYPE.surface, noone);
inputs[| 10] = nodeValue("Anti aliasing", self, JUNCTION_CONNECT.input, VALUE_TYPE.boolean, false);
//////////////////////////////////////////////////////////////////////////////////////////////////
inputs[| 11] = nodeValueMap("Scale map", self);
inputs[| 12] = nodeValueMap("Angle map", self);
inputs[| 13] = nodeValueMap("Gap map", self);
//////////////////////////////////////////////////////////////////////////////////////////////////
inputs[| 14] = nodeValue("Truchet", self, JUNCTION_CONNECT.input, VALUE_TYPE.boolean, false);
inputs[| 15] = nodeValue("Truchet seed", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, seed_random());
inputs[| 16] = nodeValue("Truchet threshold", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0.5)
.setDisplay(VALUE_DISPLAY.slider)
//////////////////////////////////////////////////////////////////////////////////////////////////
inputs[| 17] = nodeValueMap("Gradient map", self);
inputs[| 18] = nodeValueGradientRange("Gradient map range", self, inputs[| 5]);
//////////////////////////////////////////////////////////////////////////////////////////////////
inputs[| 19] = nodeValue("Texture angle", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, [ 0, 0 ])
.setDisplay(VALUE_DISPLAY.rotation_range);
input_display_list = [
["Output", false], 0,
["Pattern", false], 1, 3, 12, 2, 11, 4, 13,
["Render", false], 7, 8, 5, 17, 6, 9, 10,
["Truchet", true, 14], 15, 16, 19,
];
outputs[| 0] = nodeValue("Surface out", self, JUNCTION_CONNECT.output, VALUE_TYPE.surface, noone);
attribute_surface_depth();
static drawOverlay = function(hover, active, _x, _y, _s, _mx, _my, _snx, _sny) {
var a = inputs[| 1].drawOverlay(hover, active, _x, _y, _s, _mx, _my, _snx, _sny); active &= !a;
var a = inputs[| 18].drawOverlay(hover, active, _x, _y, _s, _mx, _my, _snx, _sny, getSingleValue(0)); active &= !a;
}
static step = function() { #region
inputs[| 2].mappableStep();
inputs[| 3].mappableStep();
inputs[| 4].mappableStep();
inputs[| 5].mappableStep();
} #endregion
static processData = function(_outSurf, _data, _output_index, _array_index) {
var _dim = _data[0];
var _pos = _data[1];
var _sam = _data[9];
var _mode = _data[7];
var _col_gap = _data[6];
inputs[| 5].setVisible(_mode == 0);
inputs[| 6].setVisible(_mode != 1);
inputs[| 9].setVisible(_mode == 2 || _mode == 3);
_outSurf = surface_verify(_outSurf, _dim[0], _dim[1], attrDepth());
surface_set_shader(_outSurf, sh_random_tile);
shader_set_f("dimension", _dim[0], _dim[1]);
shader_set_f("position", _pos[0] / _dim[0], _pos[1] / _dim[1]);
shader_set_f_map("scale", _data[ 2], _data[11], inputs[| 2]);
shader_set_f_map("angle", _data[ 3], _data[12], inputs[| 3]);
shader_set_f_map("thick", _data[ 4], _data[13], inputs[| 4]);
shader_set_f("seed", _data[ 8]);
shader_set_i("mode", _mode);
shader_set_i("aa", _data[10]);
shader_set_color("gapCol", _col_gap);
shader_set_i("textureTruchet", _data[14]);
shader_set_f("truchetSeed", _data[15]);
shader_set_f("truchetThres", _data[16]);
shader_set_f("truchetAngle", _data[19]);
shader_set_gradient(_data[5], _data[17], _data[18], inputs[| 5]);
if(is_surface(_sam)) draw_surface_stretched_safe(_sam, 0, 0, _dim[0], _dim[1]);
else draw_sprite_ext(s_fx_pixel, 0, 0, 0, _dim[0], _dim[1], 0, c_white, 1);
surface_reset_shader();
return _outSurf;
}
}

View file

@ -0,0 +1,129 @@
// 2024-05-01 09:29:37
function Node_Random_Tile(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) constructor {
name = "Random Tile";
inputs[| 0] = nodeValue("Dimension", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, DEF_SURF )
.setDisplay(VALUE_DISPLAY.vector);
inputs[| 1] = nodeValue("Position", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, [ 0, 0 ])
.setDisplay(VALUE_DISPLAY.vector)
.setUnitRef(function(index) { return getDimension(index); });
inputs[| 2] = nodeValue("Scale", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, [ 2, 2 ])
.setDisplay(VALUE_DISPLAY.vector)
.setMappable(11);
inputs[| 3] = nodeValue("Angle", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0)
.setDisplay(VALUE_DISPLAY.rotation)
.setMappable(12);
inputs[| 4] = nodeValue("Gap", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0.1)
.setDisplay(VALUE_DISPLAY.slider, { range: [0, 0.5, 0.001] })
.setMappable(13);
inputs[| 5] = nodeValue("Tile color", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white) )
.setMappable(17);
inputs[| 6] = nodeValue("Gap color", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, c_black);
inputs[| 7] = nodeValue("Render type", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, 0)
.setDisplay(VALUE_DISPLAY.enum_scroll, ["Colored tile", "Height map", "Texture grid"]);
inputs[| 8] = nodeValue("Seed", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, irandom_range(10000, 99999));
inputs[| 9] = nodeValue("Texture", self, JUNCTION_CONNECT.input, VALUE_TYPE.surface, noone);
inputs[| 10] = nodeValue("Anti aliasing", self, JUNCTION_CONNECT.input, VALUE_TYPE.boolean, false);
//////////////////////////////////////////////////////////////////////////////////////////////////
inputs[| 11] = nodeValueMap("Scale map", self);
inputs[| 12] = nodeValueMap("Angle map", self);
inputs[| 13] = nodeValueMap("Gap map", self);
//////////////////////////////////////////////////////////////////////////////////////////////////
inputs[| 14] = nodeValue("Truchet", self, JUNCTION_CONNECT.input, VALUE_TYPE.boolean, false);
inputs[| 15] = nodeValue("Truchet seed", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, seed_random());
inputs[| 16] = nodeValue("Truchet threshold", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0.5)
.setDisplay(VALUE_DISPLAY.slider)
//////////////////////////////////////////////////////////////////////////////////////////////////
inputs[| 17] = nodeValueMap("Gradient map", self);
inputs[| 18] = nodeValueGradientRange("Gradient map range", self, inputs[| 5]);
//////////////////////////////////////////////////////////////////////////////////////////////////
inputs[| 19] = nodeValue("Texture angle", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, [ 0, 0 ])
.setDisplay(VALUE_DISPLAY.rotation_range);
input_display_list = [
["Output", false], 0,
["Pattern", false], 1, 3, 12, 2, 11, 4, 13,
["Render", false], 7, 8, 5, 17, 6, 9, 10,
["Truchet", true, 14], 15, 16, 19,
];
outputs[| 0] = nodeValue("Surface out", self, JUNCTION_CONNECT.output, VALUE_TYPE.surface, noone);
attribute_surface_depth();
static drawOverlay = function(hover, active, _x, _y, _s, _mx, _my, _snx, _sny) {
var a = inputs[| 1].drawOverlay(hover, active, _x, _y, _s, _mx, _my, _snx, _sny); active &= !a;
var a = inputs[| 18].drawOverlay(hover, active, _x, _y, _s, _mx, _my, _snx, _sny, getSingleValue(0)); active &= !a;
}
static step = function() { #region
inputs[| 2].mappableStep();
inputs[| 3].mappableStep();
inputs[| 4].mappableStep();
inputs[| 5].mappableStep();
} #endregion
static processData = function(_outSurf, _data, _output_index, _array_index) {
var _dim = _data[0];
var _pos = _data[1];
var _sam = _data[9];
var _mode = _data[7];
var _col_gap = _data[6];
inputs[| 5].setVisible(_mode == 0);
inputs[| 6].setVisible(_mode != 1);
inputs[| 9].setVisible(_mode == 2 || _mode == 3);
_outSurf = surface_verify(_outSurf, _dim[0], _dim[1], attrDepth());
surface_set_shader(_outSurf, sh_random_tile);
shader_set_f("dimension", _dim[0], _dim[1]);
shader_set_f("position", _pos[0] / _dim[0], _pos[1] / _dim[1]);
shader_set_f_map("scale", _data[ 2], _data[11], inputs[| 2]);
shader_set_f_map("angle", _data[ 3], _data[12], inputs[| 3]);
shader_set_f_map("thick", _data[ 4], _data[13], inputs[| 4]);
shader_set_f("seed", _data[ 8]);
shader_set_i("mode", _mode);
shader_set_i("aa", _data[10]);
shader_set_color("gapCol", _col_gap);
shader_set_i("textureTruchet", _data[14]);
shader_set_f("truchetSeed", _data[15]);
shader_set_f("truchetThres", _data[16]);
shader_set_f("truchetAngle", _data[19]);
shader_set_gradient(_data[5], _data[17], _data[18], inputs[| 5]);
if(is_surface(_sam)) draw_surface_stretched_safe(_sam, 0, 0, _dim[0], _dim[1]);
else draw_sprite_ext(s_fx_pixel, 0, 0, 0, _dim[0], _dim[1], 0, c_white, 1);
surface_reset_shader();
return _outSurf;
}
}

View file

@ -1,4 +1,4 @@
// 2024-04-28 11:37:34 // 2024-05-01 19:28:09
#region ---- global names ---- #region ---- global names ----
global.junctionEndName = [ "Hold", "Loop", "Ping pong", "Wrap" ]; global.junctionEndName = [ "Hold", "Loop", "Ping pong", "Wrap" ];
@ -1152,7 +1152,7 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
}); });
extract_node = "Node_Vector4"; extract_node = "Node_Vector4";
display_data.angle_display = QUARTERNION_DISPLAY.quarterion; display_data.angle_display = QUARTERNION_DISPLAY.euler;
break; #endregion break; #endregion
case VALUE_DISPLAY.path_anchor : #region case VALUE_DISPLAY.path_anchor : #region
@ -1552,19 +1552,6 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
return applyUnit? unit.apply(value, arrIndex) : value; return applyUnit? unit.apply(value, arrIndex) : value;
} #endregion } #endregion
if(display_type == VALUE_DISPLAY.d3quarternion) { #region
if(!applyUnit) return value;
var dispType = display_data.angle_display;
switch(dispType) {
case QUARTERNION_DISPLAY.quarterion :
return value;
case QUARTERNION_DISPLAY.euler :
var euler = new BBMOD_Quaternion().FromEuler(value[0], value[1], value[2]).ToArray();
return euler;
}
} #endregion
if(type == VALUE_TYPE.text) { #region if(type == VALUE_TYPE.text) { #region
switch(display_type) { switch(display_type) {
case VALUE_DISPLAY.text_array : return value; case VALUE_DISPLAY.text_array : return value;
@ -1911,6 +1898,13 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
} else } else
val = ds_list_empty(animator.values)? 0 : animator.processType(animator.values[| 0].value); val = ds_list_empty(animator.values)? 0 : animator.processType(animator.values[| 0].value);
if(display_type == VALUE_DISPLAY.d3quarternion) {
switch(display_data.angle_display) {
case QUARTERNION_DISPLAY.quarterion : return val;
case QUARTERNION_DISPLAY.euler : return new BBMOD_Quaternion(val[0], val[1], val[2], val[3]).ToEuler(true);
}
}
return val; return val;
} #endregion } #endregion
@ -1993,6 +1987,7 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
static setValueInspector = function(val = 0, index = noone) { #region static setValueInspector = function(val = 0, index = noone) { #region
INLINE INLINE
var res = false; var res = false;
val = unit.invApply(val); val = unit.invApply(val);
@ -2003,27 +1998,32 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
var _node = PANEL_INSPECTOR.inspectings[i]; var _node = PANEL_INSPECTOR.inspectings[i];
if(ind >= ds_list_size(_node.inputs)) continue; if(ind >= ds_list_size(_node.inputs)) continue;
var r = _node.inputs[| ind].setValueDirect(val, index); var r = _node.inputs[| ind].setValueInspectorDirect(val, index);
if(_node == node) res = r; if(_node == node) res = r;
} }
} else { } else {
res = setValueDirect(val, index); res = setValueInspectorDirect(val, index);
//print($"Node {node} : {node.name} {node.internalName}");
//print($"Inspecting {PANEL_INSPECTOR.inspecting} : {PANEL_INSPECTOR.inspecting.name} {PANEL_INSPECTOR.inspecting.internalName}");
//print($"{node == PANEL_INSPECTOR.inspecting}");
//print("");
} }
return res; return res;
} #endregion } #endregion
static setValueInspectorDirect = function(val = 0, index = noone) {
if(display_type == VALUE_DISPLAY.d3quarternion && display_data.angle_display == QUARTERNION_DISPLAY.euler) {
var _qval = new BBMOD_Quaternion().FromEuler(-val[0], -val[1], -val[2]).ToArray();
var _eval = new BBMOD_Quaternion(_qval[0], _qval[1], _qval[2], _qval[3]).ToEuler(true);
return setValueDirect(_qval);
}
return setValueDirect(val, index);
}
static setValueDirect = function(val = 0, index = noone, record = true, time = CURRENT_FRAME, _update = true) { #region static setValueDirect = function(val = 0, index = noone, record = true, time = CURRENT_FRAME, _update = true) { #region
is_modified = true; is_modified = true;
var updated = false; var updated = false;
var _val = val; var _val = val;
var _inp = connect_type == JUNCTION_CONNECT.input; var _inp = connect_type == JUNCTION_CONNECT.input;
if(sep_axis) { if(sep_axis) {
if(index == noone) { if(index == noone) {
for( var i = 0, n = array_length(animators); i < n; i++ ) for( var i = 0, n = array_length(animators); i < n; i++ )
@ -2039,8 +2039,7 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
_val[index] = val; _val[index] = val;
} }
updated = animator.setValue(_val, _inp && record, time); updated = animator.setValue(_val, _inp && record, time);
//print($"{updated}: {index} - {_val}");
} }
if(type == VALUE_TYPE.gradient) updated = true; if(type == VALUE_TYPE.gradient) updated = true;

View file

@ -1,4 +1,4 @@
// 2024-04-28 11:29:46 // 2024-05-01 19:27:29
#region ---- global names ---- #region ---- global names ----
global.junctionEndName = [ "Hold", "Loop", "Ping pong", "Wrap" ]; global.junctionEndName = [ "Hold", "Loop", "Ping pong", "Wrap" ];
@ -1152,7 +1152,7 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
}); });
extract_node = "Node_Vector4"; extract_node = "Node_Vector4";
display_data.angle_display = QUARTERNION_DISPLAY.quarterion; display_data.angle_display = QUARTERNION_DISPLAY.euler;
break; #endregion break; #endregion
case VALUE_DISPLAY.path_anchor : #region case VALUE_DISPLAY.path_anchor : #region
@ -1552,19 +1552,6 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
return applyUnit? unit.apply(value, arrIndex) : value; return applyUnit? unit.apply(value, arrIndex) : value;
} #endregion } #endregion
if(display_type == VALUE_DISPLAY.d3quarternion) { #region
if(!applyUnit) return value;
var dispType = display_data.angle_display;
switch(dispType) {
case QUARTERNION_DISPLAY.quarterion :
return value;
case QUARTERNION_DISPLAY.euler :
var euler = new BBMOD_Quaternion().FromEuler(value[0], value[1], value[2]).ToArray();
return euler;
}
} #endregion
if(type == VALUE_TYPE.text) { #region if(type == VALUE_TYPE.text) { #region
switch(display_type) { switch(display_type) {
case VALUE_DISPLAY.text_array : return value; case VALUE_DISPLAY.text_array : return value;
@ -1911,6 +1898,13 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
} else } else
val = ds_list_empty(animator.values)? 0 : animator.processType(animator.values[| 0].value); val = ds_list_empty(animator.values)? 0 : animator.processType(animator.values[| 0].value);
if(display_type == VALUE_DISPLAY.d3quarternion) {
switch(display_data.angle_display) {
case QUARTERNION_DISPLAY.quarterion : return val;
case QUARTERNION_DISPLAY.euler : return new BBMOD_Quaternion(val[0], val[1], val[2], val[3]).ToEuler(true);
}
}
return val; return val;
} #endregion } #endregion
@ -1993,6 +1987,7 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
static setValueInspector = function(val = 0, index = noone) { #region static setValueInspector = function(val = 0, index = noone) { #region
INLINE INLINE
var res = false; var res = false;
val = unit.invApply(val); val = unit.invApply(val);
@ -2003,27 +1998,32 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
var _node = PANEL_INSPECTOR.inspectings[i]; var _node = PANEL_INSPECTOR.inspectings[i];
if(ind >= ds_list_size(_node.inputs)) continue; if(ind >= ds_list_size(_node.inputs)) continue;
var r = _node.inputs[| ind].setValueDirect(val, index); var r = _node.inputs[| ind].setValueInspectorDirect(val, index);
if(_node == node) res = r; if(_node == node) res = r;
} }
} else { } else {
res = setValueDirect(val, index); res = setValueInspectorDirect(val, index);
//print($"Node {node} : {node.name} {node.internalName}");
//print($"Inspecting {PANEL_INSPECTOR.inspecting} : {PANEL_INSPECTOR.inspecting.name} {PANEL_INSPECTOR.inspecting.internalName}");
//print($"{node == PANEL_INSPECTOR.inspecting}");
//print("");
} }
return res; return res;
} #endregion } #endregion
static setValueInspectorDirect = function(val = 0, index = noone) {
if(display_type == VALUE_DISPLAY.d3quarternion && display_data.angle_display == QUARTERNION_DISPLAY.euler) {
var _qval = new BBMOD_Quaternion().FromEuler(-val[0], -val[1], -val[2]).ToArray();
var _eval = new BBMOD_Quaternion(_qval[0], _qval[1], _qval[2], _qval[3]).ToEuler(true);
return setValueDirect(_qval);
}
return setValueDirect(val, index);
}
static setValueDirect = function(val = 0, index = noone, record = true, time = CURRENT_FRAME, _update = true) { #region static setValueDirect = function(val = 0, index = noone, record = true, time = CURRENT_FRAME, _update = true) { #region
is_modified = true; is_modified = true;
var updated = false; var updated = false;
var _val = val; var _val = val;
var _inp = connect_type == JUNCTION_CONNECT.input; var _inp = connect_type == JUNCTION_CONNECT.input;
if(sep_axis) { if(sep_axis) {
if(index == noone) { if(index == noone) {
for( var i = 0, n = array_length(animators); i < n; i++ ) for( var i = 0, n = array_length(animators); i < n; i++ )
@ -2039,8 +2039,7 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
_val[index] = val; _val[index] = val;
} }
updated = animator.setValue(_val, _inp && record, time); updated = animator.setValue(_val, _inp && record, time);
//print($"{updated}: {index} - {_val}");
} }
if(type == VALUE_TYPE.gradient) updated = true; if(type == VALUE_TYPE.gradient) updated = true;

View file

@ -0,0 +1,295 @@
// 2024-05-01 13:45:32
function readObj_init(_scale = 1) {
obj_reading = true;
obj_read_progress = 0;
obj_read_prog_sub = 0;
obj_read_prog_tot = 3;
obj_raw = noone;
obj_reading_scale = _scale;
_VB = [];
_VBT = [];
_VBN = [];
mats = [];
matIndex = [];
tris = [];
mtlPath = "";
use_normal = true;
v = ds_list_create();
vt = ds_list_create();
vn = ds_list_create();
f = ds_list_create();
ft = ds_list_create();
fn = ds_list_create();
tri = 0;
}
function readObj_file() {
var _time = current_time;
while(!file_text_eof(obj_read_file)) { #region reading file
var l = file_text_readln(obj_read_file);
l = string_trim(l);
var sep = string_split(l, " ");
if(array_length(sep) == 0) continue;
switch(sep[0]) {
case "v" :
ds_list_add(v, [
toNumber(sep[1]) * obj_reading_scale,
toNumber(sep[2]) * obj_reading_scale,
toNumber(sep[3]) * obj_reading_scale
]);
break;
case "vt" :
var _u = toNumber(sep[1]);
var _v = toNumber(sep[2]);
ds_list_add(vt, [ _u, _v ]);
break;
case "vn" :
var _nx = toNumber(sep[1]);
var _ny = toNumber(sep[2]);
var _nz = toNumber(sep[3]);
ds_list_add(vn, [ _nx, _ny, _nz ]);
break;
case "f" :
var _len = array_length(sep);
var _f = array_create(_len - 1);
var _ft = array_create(_len - 1);
var _fn = array_create(_len - 1);
for( var i = 1; i < _len; i++ ) {
var _sp = string_split(sep[i], "/");
if(array_length(_sp) < 3) continue;
_f[i - 1] = toNumber(_sp[0]);
_ft[i - 1] = toNumber(_sp[1]);
_fn[i - 1] = toNumber(_sp[2]);
use_normal = array_length(_sp) >= 4;
}
tri += _len - 2;
ds_list_add(f, _f ); //get position
ds_list_add(ft, _ft); //get texture map
ds_list_add(fn, _fn); //get normal
break;
case "usemtl" :
var mname = "";
for( var i = 1; i < array_length(sep); i++ )
mname += (i == 1? "" : " ") + sep[i];
mname = string_trim(mname);
array_push_unique(mats, mname);
array_push(matIndex, array_find(mats, mname));
if(!ds_list_empty(f)) {
array_push(_VB, f);
array_push(_VBT, ft);
array_push(_VBN, fn);
array_push(tris, tri);
f = ds_list_create();
ft = ds_list_create();
fn = ds_list_create();
}
tri = 0;
break;
case "mtllib" :
mtlPath = "";
for( var i = 1; i < array_length(sep); i++ )
mtlPath += (i == 1? "" : " ") + sep[i];
mtlPath = string_trim(mtlPath);
break;
case "o" :
//print("Reading vertex group: " + sep[1])
break;
}
if(current_time - _time > 30) return;
} #endregion
if(!ds_list_empty(f)) {
array_push(_VB, f);
array_push(_VBT, ft);
array_push(_VBN, fn);
array_push(tris, tri);
}
file_text_close(obj_read_file);
if(use_normal) vn[| 0] = [ 0, 0, 0 ];
obj_read_progress = 1;
obj_read_prog_sub = 0;
//var txt = "OBJ summary";
//txt += $"\n\tVerticies : {ds_list_size(v)}";
//txt += $"\n\tTexture Verticies : {ds_list_size(vt)}";
//txt += $"\n\tNormal Verticies : {ds_list_size(vn)}";
//txt += $"\n\tVertex groups : {array_length(_VB)}";
//txt += $"\n\tTriangles : {tris}";
//print(txt);
}
function readObj_cent() {
#region centralize vertex
_bmin = v[| 0];
_bmax = v[| 0];
cv = [0, 0, 0];
vertex = ds_list_size(v);
for( var i = 0; i < vertex; i++ ) {
var _v = v[| i];
var _v0 = _v[0];
var _v1 = _v[1];
var _v2 = _v[2];
cv[0] += _v0;
cv[1] += _v1;
cv[2] += _v2;
_bmin = [
min(_bmin[0], _v0),
min(_bmin[1], _v1),
min(_bmin[2], _v2),
];
_bmax = [
max(_bmax[0], _v0),
max(_bmax[1], _v1),
max(_bmax[2], _v2),
];
}
cv[0] /= vertex;
cv[1] /= vertex;
cv[2] /= vertex;
obj_size = new __vec3(
_bmax[0] - _bmin[0],
_bmax[1] - _bmin[1],
_bmax[2] - _bmin[2],
);
//print($"{obj_size}");
var sc = 1;
//var span = max(abs(_size.x), abs(_size.y), abs(_size.z));
//if(span > 10) sc = span / 10;
for( var i = 0, n = ds_list_size(v); i < n; i++ ) {
v[| i][0] = (v[| i][0] - cv[0]) / sc;
v[| i][1] = (v[| i][1] - cv[1]) / sc;
v[| i][2] = (v[| i][2] - cv[2]) / sc;
}
#endregion
obj_read_progress = 2;
obj_read_prog_sub = 0;
}
function readObj_buff() {
#region vertex buffer creation
var _vblen = array_length(_VB);
var VBS = array_create(_vblen);
var V = array_create(_vblen);
for(var i = 0; i < _vblen; i++) {
var VB = vertex_create_buffer();
vertex_begin(VB, global.VF_POS_NORM_TEX_COL);
var face = _VB[i];
var facet = _VBT[i];
var facen = _VBN[i];
var _flen = ds_list_size(face);
var _v = ds_list_create();
for(var j = 0; j < _flen; j++) {
var _f = face[| j];
var _ft = facet[| j];
var _fn = facen[| j];
var _vlen = array_length(_f);
var _pf = array_create(_vlen);
var _pft = array_create(_vlen);
var _pfn = array_create(_vlen);
for( var k = 0; k < _vlen; k++ ) {
var _vPindex = _f[k] - 1;
_pf[k] = v[| _vPindex];
var _vNindex = _fn[k] - 1;
_pfn[k] = vn[| _vNindex];
var _vTindex = _ft[k] - 1;
_pft[k] = vt[| _vTindex];
}
if(_vlen >= 3) {
vertex_add_pntc(VB, _pf[0], _pfn[0], _pft[0]);
vertex_add_pntc(VB, _pf[2], _pfn[2], _pft[2]);
vertex_add_pntc(VB, _pf[1], _pfn[1], _pft[1]);
ds_list_add(_v, new __vertex(_pf[0][0], _pf[0][1], _pf[0][2]).setNormal(_pfn[0][0], _pfn[0][1]).setUV(_pft[0][0], _pft[0][1]));
ds_list_add(_v, new __vertex(_pf[2][0], _pf[2][1], _pf[2][2]).setNormal(_pfn[2][0], _pfn[2][1]).setUV(_pft[2][0], _pft[2][1]));
ds_list_add(_v, new __vertex(_pf[1][0], _pf[1][1], _pf[1][2]).setNormal(_pfn[1][0], _pfn[1][1]).setUV(_pft[1][0], _pft[1][1]));
}
if(_vlen >= 4) {
vertex_add_pntc(VB, _pf[0], _pfn[0], _pft[0]);
vertex_add_pntc(VB, _pf[3], _pfn[3], _pft[3]);
vertex_add_pntc(VB, _pf[2], _pfn[2], _pft[2]);
ds_list_add(_v, new __vertex(_pf[0][0], _pf[0][1], _pf[0][2]).setNormal(_pfn[0][0], _pfn[0][1]).setUV(_pft[0][0], _pft[0][1]));
ds_list_add(_v, new __vertex(_pf[3][0], _pf[3][1], _pf[3][2]).setNormal(_pfn[3][0], _pfn[3][1]).setUV(_pft[3][0], _pft[3][1]));
ds_list_add(_v, new __vertex(_pf[2][0], _pf[2][1], _pf[2][2]).setNormal(_pfn[2][0], _pfn[2][1]).setUV(_pft[2][0], _pft[2][1]));
}
}
vertex_end(VB);
vertex_freeze(VB);
VBS[i] = VB;
V[i] = ds_list_to_array(_v);
ds_list_destroy(_v);
}
#endregion
#region clean
array_foreach(_VB, function(val, ind) { ds_list_destroy(val); });
array_foreach(_VBT, function(val, ind) { ds_list_destroy(val); });
array_foreach(_VBN, function(val, ind) { ds_list_destroy(val); });
ds_list_destroy(v);
ds_list_destroy(vn);
ds_list_destroy(vt);
#endregion
obj_read_progress = 3;
obj_read_prog_sub = 0;
obj_raw = {
vertex: V,
vertex_count: vertex,
vertex_groups: VBS,
object_counts: _vblen,
materials: mats,
material_index: matIndex,
use_normal: use_normal,
mtl_path: mtlPath,
model_size: obj_size,
};
}

View file

@ -0,0 +1,295 @@
// 2024-05-01 13:44:23
function readObj_init(_scale = 1) {
obj_reading = true;
obj_read_progress = 0;
obj_read_prog_sub = 0;
obj_read_prog_tot = 3;
obj_raw = noone;
obj_reading_scale = _scale;
_VB = [];
_VBT = [];
_VBN = [];
mats = [];
matIndex = [];
tris = [];
mtlPath = "";
use_normal = true;
v = ds_list_create();
vt = ds_list_create();
vn = ds_list_create();
f = ds_list_create();
ft = ds_list_create();
fn = ds_list_create();
tri = 0;
}
function readObj_file() {
var _time = current_time;
while(!file_text_eof(obj_read_file)) { #region reading file
var l = file_text_readln(obj_read_file);
l = string_trim(l);
var sep = string_split(l, " ");
if(array_length(sep) == 0) continue;
switch(sep[0]) {
case "v" :
ds_list_add(v, [
toNumber(sep[1]) * obj_reading_scale,
toNumber(sep[2]) * obj_reading_scale,
toNumber(sep[3]) * obj_reading_scale
]);
break;
case "vt" :
var _u = toNumber(sep[1]);
var _v = toNumber(sep[2]);
ds_list_add(vt, [ _u, _v ]);
break;
case "vn" :
var _nx = toNumber(sep[1]);
var _ny = toNumber(sep[2]);
var _nz = toNumber(sep[3]);
ds_list_add(vn, [ _nx, _ny, _nz ]);
break;
case "f" :
var _len = array_length(sep);
var _f = array_create(_len - 1);
var _ft = array_create(_len - 1);
var _fn = array_create(_len - 1);
for( var i = 1; i < _len; i++ ) {
var _sp = string_split(sep[i], "/");
if(array_length(_sp) < 3) continue;
_f[i - 1] = toNumber(_sp[0]);
_ft[i - 1] = toNumber(_sp[1]);
_fn[i - 1] = toNumber(_sp[2]);
use_normal = array_length(_sp) >= 4;
}
tri += _len - 2;
ds_list_add(f, _f ); //get position
ds_list_add(ft, _ft); //get texture map
ds_list_add(fn, _fn); //get normal
break;
case "usemtl" :
var mname = "";
for( var i = 1; i < array_length(sep); i++ )
mname += (i == 1? "" : " ") + sep[i];
mname = string_trim(mname);
array_push_unique(mats, mname);
array_push(matIndex, array_find(mats, mname));
if(!ds_list_empty(f)) {
array_push(_VB, f);
array_push(_VBT, ft);
array_push(_VBN, fn);
array_push(tris, tri);
f = ds_list_create();
ft = ds_list_create();
fn = ds_list_create();
}
tri = 0;
break;
case "mtllib" :
mtlPath = "";
for( var i = 1; i < array_length(sep); i++ )
mtlPath += (i == 1? "" : " ") + sep[i];
mtlPath = string_trim(mtlPath);
break;
case "o" :
//print("Reading vertex group: " + sep[1])
break;
}
if(current_time - _time > 30) return;
} #endregion
if(!ds_list_empty(f)) {
array_push(_VB, f);
array_push(_VBT, ft);
array_push(_VBN, fn);
array_push(tris, tri);
}
file_text_close(obj_read_file);
if(use_normal) vn[| 0] = [ 0, 0, 0 ];
obj_read_progress = 1;
obj_read_prog_sub = 0;
//var txt = "OBJ summary";
//txt += $"\n\tVerticies : {ds_list_size(v)}";
//txt += $"\n\tTexture Verticies : {ds_list_size(vt)}";
//txt += $"\n\tNormal Verticies : {ds_list_size(vn)}";
//txt += $"\n\tVertex groups : {array_length(_VB)}";
//txt += $"\n\tTriangles : {tris}";
//print(txt);
}
function readObj_cent() {
#region centralize vertex
_bmin = v[| 0];
_bmax = v[| 0];
cv = [0, 0, 0];
vertex = ds_list_size(v);
for( var i = 0; i < vertex; i++ ) {
var _v = v[| i];
var _v0 = _v[0];
var _v1 = _v[1];
var _v2 = _v[2];
cv[0] += _v0;
cv[1] += _v1;
cv[2] += _v2;
_bmin = [
min(_bmin[0], _v0),
min(_bmin[1], _v1),
min(_bmin[2], _v2),
];
_bmax = [
max(_bmax[0], _v0),
max(_bmax[1], _v1),
max(_bmax[2], _v2),
];
}
cv[0] /= vertex;
cv[1] /= vertex;
cv[2] /= vertex;
obj_size = new __vec3(
_bmax[0] - _bmin[0],
_bmax[1] - _bmin[1],
_bmax[2] - _bmin[2],
);
print($"{obj_size}");
var sc = 1;
//var span = max(abs(_size.x), abs(_size.y), abs(_size.z));
//if(span > 10) sc = span / 10;
for( var i = 0, n = ds_list_size(v); i < n; i++ ) {
v[| i][0] = (v[| i][0] - cv[0]) / sc;
v[| i][1] = (v[| i][1] - cv[1]) / sc;
v[| i][2] = (v[| i][2] - cv[2]) / sc;
}
#endregion
obj_read_progress = 2;
obj_read_prog_sub = 0;
}
function readObj_buff() {
#region vertex buffer creation
var _vblen = array_length(_VB);
var VBS = array_create(_vblen);
var V = array_create(_vblen);
for(var i = 0; i < _vblen; i++) {
var VB = vertex_create_buffer();
vertex_begin(VB, global.VF_POS_NORM_TEX_COL);
var face = _VB[i];
var facet = _VBT[i];
var facen = _VBN[i];
var _flen = ds_list_size(face);
var _v = ds_list_create();
for(var j = 0; j < _flen; j++) {
var _f = face[| j];
var _ft = facet[| j];
var _fn = facen[| j];
var _vlen = array_length(_f);
var _pf = array_create(_vlen);
var _pft = array_create(_vlen);
var _pfn = array_create(_vlen);
for( var k = 0; k < _vlen; k++ ) {
var _vPindex = _f[k] - 1;
_pf[k] = v[| _vPindex];
var _vNindex = _fn[k] - 1;
_pfn[k] = vn[| _vNindex];
var _vTindex = _ft[k] - 1;
_pft[k] = vt[| _vTindex];
}
if(_vlen >= 3) {
vertex_add_pntc(VB, _pf[0], _pfn[0], _pft[0]);
vertex_add_pntc(VB, _pf[2], _pfn[2], _pft[2]);
vertex_add_pntc(VB, _pf[1], _pfn[1], _pft[1]);
ds_list_add(_v, new __vertex(_pf[0][0], _pf[0][1], _pf[0][2]).setNormal(_pfn[0][0], _pfn[0][1]).setUV(_pft[0][0], _pft[0][1]));
ds_list_add(_v, new __vertex(_pf[2][0], _pf[2][1], _pf[2][2]).setNormal(_pfn[2][0], _pfn[2][1]).setUV(_pft[2][0], _pft[2][1]));
ds_list_add(_v, new __vertex(_pf[1][0], _pf[1][1], _pf[1][2]).setNormal(_pfn[1][0], _pfn[1][1]).setUV(_pft[1][0], _pft[1][1]));
}
if(_vlen >= 4) {
vertex_add_pntc(VB, _pf[0], _pfn[0], _pft[0]);
vertex_add_pntc(VB, _pf[3], _pfn[3], _pft[3]);
vertex_add_pntc(VB, _pf[2], _pfn[2], _pft[2]);
ds_list_add(_v, new __vertex(_pf[0][0], _pf[0][1], _pf[0][2]).setNormal(_pfn[0][0], _pfn[0][1]).setUV(_pft[0][0], _pft[0][1]));
ds_list_add(_v, new __vertex(_pf[3][0], _pf[3][1], _pf[3][2]).setNormal(_pfn[3][0], _pfn[3][1]).setUV(_pft[3][0], _pft[3][1]));
ds_list_add(_v, new __vertex(_pf[2][0], _pf[2][1], _pf[2][2]).setNormal(_pfn[2][0], _pfn[2][1]).setUV(_pft[2][0], _pft[2][1]));
}
}
vertex_end(VB);
vertex_freeze(VB);
VBS[i] = VB;
V[i] = ds_list_to_array(_v);
ds_list_destroy(_v);
}
#endregion
#region clean
array_foreach(_VB, function(val, ind) { ds_list_destroy(val); });
array_foreach(_VBT, function(val, ind) { ds_list_destroy(val); });
array_foreach(_VBN, function(val, ind) { ds_list_destroy(val); });
ds_list_destroy(v);
ds_list_destroy(vn);
ds_list_destroy(vt);
#endregion
obj_read_progress = 3;
obj_read_prog_sub = 0;
obj_raw = {
vertex: V,
vertex_count: vertex,
vertex_groups: VBS,
object_counts: _vblen,
materials: mats,
material_index: matIndex,
use_normal: use_normal,
mtl_path: mtlPath,
model_size: obj_size,
};
}

View file

@ -1,4 +1,4 @@
// 2024-04-30 16:02:42 // 2024-05-01 09:02:09
#region funtion calls #region funtion calls
function __fnInit_Graph() { function __fnInit_Graph() {
__registerFunction("graph_add_node", panel_graph_add_node); __registerFunction("graph_add_node", panel_graph_add_node);
@ -167,7 +167,7 @@ function Panel_Graph(project = PROJECT) : PanelContent() constructor {
#endregion #endregion
#region ---- position ---- #region ---- position ----
scale = [ 0.01, 0.02, 0.05, 0.10, 0.15, 0.20, 0.25, 0.33, 0.50, 0.65, 0.80, 1, 1.2, 1.35, 1.5, 2.0]; scale = [ 0.01, 0.02, 0.05, 0.10, 0.15, 0.20, 0.25, 0.33, 0.50, 0.65, 0.80, 1, 1.2, 1.35, 1.5, 2.0 ];
graph_s = 1; graph_s = 1;
graph_s_to = graph_s; graph_s_to = graph_s;

View file

@ -1,4 +1,4 @@
// 2024-04-30 15:49:15 // 2024-05-01 08:49:36
#region funtion calls #region funtion calls
function __fnInit_Graph() { function __fnInit_Graph() {
__registerFunction("graph_add_node", panel_graph_add_node); __registerFunction("graph_add_node", panel_graph_add_node);
@ -167,7 +167,7 @@ function Panel_Graph(project = PROJECT) : PanelContent() constructor {
#endregion #endregion
#region ---- position ---- #region ---- position ----
scale = [ 0.01, 0.02, 0.05, 0.10, 0.15, 0.20, 0.25, 0.33, 0.50, 0.65, 0.80, 1, 1.2, 1.35, 1.5, 2.0]; scale = [ 0.01, 0.02, 0.05, 0.10, 0.15, 0.20, 0.25, 0.33, 0.50, 0.65, 0.80, 1, 1.2, 1.35, 1.5, 2.0 ];
graph_s = 1; graph_s = 1;
graph_s_to = graph_s; graph_s_to = graph_s;

View file

@ -1,4 +1,4 @@
// 2024-04-30 13:08:35 // 2024-05-01 16:14:03
#region funtion calls #region funtion calls
function __fnInit_Preview() { function __fnInit_Preview() {
__registerFunction("preview_focus_content", panel_preview_focus_content); __registerFunction("preview_focus_content", panel_preview_focus_content);
@ -927,7 +927,7 @@ function Panel_Preview() : PanelContent() constructor {
#region defer #region defer
var _prev_obj = _prev_node.getPreviewObject(); var _prev_obj = _prev_node.getPreviewObject();
d3_deferData = d3_scene_preview.deferPass(_prev_obj, w, h, d3_deferData); if(_prev_obj) d3_deferData = d3_scene_preview.deferPass(_prev_obj, w, h, d3_deferData);
#endregion #endregion
#region grid #region grid
@ -1504,6 +1504,7 @@ function Panel_Preview() : PanelContent() constructor {
} }
wdg.setFocusHover(pFOCUS, pHOVER); wdg.setFocusHover(pFOCUS, pHOVER);
var _tool_font = f_p3;
switch(instanceof(wdg)) { switch(instanceof(wdg)) {
case "textBox" : case "textBox" :
@ -1512,16 +1513,27 @@ function Panel_Preview() : PanelContent() constructor {
break; break;
case "buttonGroup": case "buttonGroup":
case "checkBoxGroup" : tolw = tolh * wdg.size; break; case "checkBoxGroup" :
tolw = tolh * wdg.size;
break;
case "checkBox" : tolw = tolh; break; case "checkBox" :
case "scrollBox" : tolw = ui(96); break; tolw = tolh;
case "buttonClass" : tolw = wdg.text == ""? tolh : tolw; break; break;
case "scrollBox" :
tolw = ui(96);
_tool_font = f_p2;
break;
case "buttonClass" :
tolw = wdg.text == ""? tolh : tolw;
break;
} }
var params = new widgetParam(tolx, toly, tolw, tolh, atr[$ key],, [ mx, my ]) var params = new widgetParam(tolx, toly, tolw, tolh, atr[$ key],, [ mx, my ])
params.s = tolh; params.s = tolh;
params.font = f_p3; params.font = _tool_font;
wdg.drawParam(params); wdg.drawParam(params);

View file

@ -1,4 +1,4 @@
// 2024-04-30 13:08:33 // 2024-05-01 16:14:02
#region funtion calls #region funtion calls
function __fnInit_Preview() { function __fnInit_Preview() {
__registerFunction("preview_focus_content", panel_preview_focus_content); __registerFunction("preview_focus_content", panel_preview_focus_content);
@ -927,7 +927,7 @@ function Panel_Preview() : PanelContent() constructor {
#region defer #region defer
var _prev_obj = _prev_node.getPreviewObject(); var _prev_obj = _prev_node.getPreviewObject();
d3_deferData = d3_scene_preview.deferPass(_prev_obj, w, h, d3_deferData); if(_prev_obj) d3_deferData = d3_scene_preview.deferPass(_prev_obj, w, h, d3_deferData);
#endregion #endregion
#region grid #region grid
@ -1504,6 +1504,7 @@ function Panel_Preview() : PanelContent() constructor {
} }
wdg.setFocusHover(pFOCUS, pHOVER); wdg.setFocusHover(pFOCUS, pHOVER);
var _tool_font = f_p3;
switch(instanceof(wdg)) { switch(instanceof(wdg)) {
case "textBox" : case "textBox" :
@ -1512,16 +1513,27 @@ function Panel_Preview() : PanelContent() constructor {
break; break;
case "buttonGroup": case "buttonGroup":
case "checkBoxGroup" : tolw = tolh * wdg.size; break; case "checkBoxGroup" :
tolw = tolh * wdg.size;
break;
case "checkBox" : tolw = tolh; break; case "checkBox" :
case "scrollBox" : tolw = ui(96); break; tolw = tolh;
case "buttonClass" : tolw = wdg.text == ""? tolh : tolw; break; break;
case "scrollBox" :
tolw = ui(96);
_tool_font = f_p2;
break;
case "buttonClass" :
tolw = wdg.text == ""? tolh : tolw;
break;
} }
var params = new widgetParam(tolx, toly, tolw, tolh, atr[$ key],, [ mx, my ]) var params = new widgetParam(tolx, toly, tolw, tolh, atr[$ key],, [ mx, my ])
params.s = tolh; params.s = tolh;
params.font = f_p3; params.font = _tool_font;
wdg.drawParam(params); wdg.drawParam(params);

View file

@ -1,4 +1,4 @@
// 2024-04-29 18:32:52 // 2024-05-01 16:00:48
#region preference #region preference
globalvar PREFERENCES, PREFERENCES_DEF, HOTKEYS_DATA; globalvar PREFERENCES, PREFERENCES_DEF, HOTKEYS_DATA;
PREFERENCES = {}; PREFERENCES = {};
@ -114,6 +114,7 @@
PREFERENCES.node_param_show = false; PREFERENCES.node_param_show = false;
PREFERENCES.node_param_width = 192; PREFERENCES.node_param_width = 192;
PREFERENCES.node_3d_preview_size = 256;
#endregion #endregion

View file

@ -1,4 +1,4 @@
// 2024-04-28 08:01:20 // 2024-05-01 15:57:54
#region preference #region preference
globalvar PREFERENCES, PREFERENCES_DEF, HOTKEYS_DATA; globalvar PREFERENCES, PREFERENCES_DEF, HOTKEYS_DATA;
PREFERENCES = {}; PREFERENCES = {};
@ -114,6 +114,7 @@
PREFERENCES.node_param_show = false; PREFERENCES.node_param_show = false;
PREFERENCES.node_param_width = 192; PREFERENCES.node_param_width = 192;
PREFERENCES.node_3d_preview_size = 256;
#endregion #endregion

View file

@ -0,0 +1,151 @@
// 2024-05-01 17:23:09
enum QUARTERNION_DISPLAY {
quarterion,
euler,
}
function quarternionBox(_onModify) : widget() constructor {
onModify = _onModify;
current_value = [ 0, 0, 0, 0 ];
current_unit = QUARTERNION_DISPLAY.quarterion;
onModifyIndex = function(index, val) {
var v = toNumber(val);
if(current_unit == QUARTERNION_DISPLAY.quarterion) {
return onModify(index, v);
} else {
var v = toNumber(val);
var qv = [
current_value[0],
current_value[1],
current_value[2],
];
qv[index] = v;
return onModify(noone, qv);
}
}
size = 4;
axis = [ "x", "y", "z", "w" ];
tooltip = new tooltipSelector("Angle type", [__txt("Quaternion"), __txt("Euler")]);
disp_w = noone;
clickable = true;
onModifySingle[0] = function(val) { return onModifyIndex(0, val); }
onModifySingle[1] = function(val) { return onModifyIndex(1, val); }
onModifySingle[2] = function(val) { return onModifyIndex(2, val); }
onModifySingle[3] = function(val) { return onModifyIndex(3, val); }
for(var i = 0; i < 4; i++) {
tb[i] = new textBox(TEXTBOX_INPUT.number, onModifySingle[i]);
tb[i].slidable = true;
tb[i].label = axis[i];
}
static setSlideSpeed = function(speed) {
for(var i = 0; i < size; i++)
tb[i].setSlidable(speed);
}
static setInteract = function(interactable) {
self.interactable = interactable;
for( var i = 0; i < size; i++ )
tb[i].interactable = interactable;
}
static register = function(parent = noone) {
for( var i = 0; i < size; i++ )
tb[i].register(parent);
}
static isHovering = function() {
for( var i = 0, n = array_length(tb); i < n; i++ ) if(tb[i].isHovering()) return true;
return false;
}
static apply = function() {
for( var i = 0; i < size; i++ ) {
tb[i].apply();
current_value[i] = toNumber(tb[i]._input_text);
}
}
static drawParam = function(params) {
setParam(params);
for(var i = 0; i < 4; i++) tb[i].setParam(params);
return draw(params.x, params.y, params.w, params.h, params.data, params.display_data, params.m);
}
static draw = function(_x, _y, _w, _h, _data, _display_data, _m) {
x = _x;
y = _y;
w = _w;
h = _h;
if(!is_array(_data)) return 0;
if(array_empty(_data)) return 0;
if(is_array(_data[0])) return 0;
var _bs = min(_h, ui(32));
var _disp = struct_try_get(_display_data, "angle_display");
if((_w - _bs) / 2 > ui(64)) {
var bx = _x + _w - _bs;
var by = _y + _h / 2 - _bs / 2;
tooltip.index = _disp;
if(buttonInstant(THEME.button_hide, bx, by, _bs, _bs, _m, iactive, ihover, tooltip, THEME.unit_angle, _disp, c_white) == 2) {
clickable = false;
_display_data.angle_display = (_disp + 1) % 2;
}
_w -= _bs + ui(8);
}
current_unit = _display_data.angle_display;
if(current_unit == QUARTERNION_DISPLAY.quarterion || (!tb[0].sliding && !tb[1].sliding && !tb[2].sliding)) {
current_value[0] = _data[0];
current_value[1] = _data[1];
current_value[2] = _data[2];
if(current_unit == QUARTERNION_DISPLAY.quarterion)
current_value[3] = _data[3];
}
size = _disp? 3 : 4;
var ww = _w / size;
var bx = _x;
disp_w = disp_w == noone? ww : lerp_float(disp_w, ww, 3);
var _dispDat = _data;
draw_sprite_stretched_ext(THEME.textbox, 3, _x, _y, _w, _h, c_white, 1);
draw_sprite_stretched_ext(THEME.textbox, 0, _x, _y, _w, _h, c_white, 0.5 + 0.5 * interactable);
for(var i = 0; i < size; i++) {
var _a = _dispDat[i];
tb[i].hide = true;
tb[i].setFocusHover(clickable && active, hover);
tb[i].draw(bx, _y, disp_w, _h, _a, _m);
bx += disp_w;
}
clickable = true;
resetFocus();
return _h;
}
static clone = function() { #region
var cln = new quarternionBox(onModify);
return cln;
} #endregion
}

View file

@ -0,0 +1,152 @@
// 2024-05-01 17:22:26
enum QUARTERNION_DISPLAY {
quarterion,
euler,
}
function quarternionBox(_onModify) : widget() constructor {
onModify = _onModify;
current_value = [ 0, 0, 0, 0 ];
current_unit = QUARTERNION_DISPLAY.quarterion;
onModifyIndex = function(index, val) {
var v = toNumber(val);
if(current_unit == QUARTERNION_DISPLAY.quarterion) {
return onModify(index, v);
} else {
var v = toNumber(val);
var qv = [
current_value[0],
current_value[1],
current_value[2],
];
qv[index] = v;
print(qv);
return onModify(noone, qv);
}
}
size = 4;
axis = [ "x", "y", "z", "w" ];
tooltip = new tooltipSelector("Angle type", [__txt("Quaternion"), __txt("Euler")]);
disp_w = noone;
clickable = true;
onModifySingle[0] = function(val) { return onModifyIndex(0, val); }
onModifySingle[1] = function(val) { return onModifyIndex(1, val); }
onModifySingle[2] = function(val) { return onModifyIndex(2, val); }
onModifySingle[3] = function(val) { return onModifyIndex(3, val); }
for(var i = 0; i < 4; i++) {
tb[i] = new textBox(TEXTBOX_INPUT.number, onModifySingle[i]);
tb[i].slidable = true;
tb[i].label = axis[i];
}
static setSlideSpeed = function(speed) {
for(var i = 0; i < size; i++)
tb[i].setSlidable(speed);
}
static setInteract = function(interactable) {
self.interactable = interactable;
for( var i = 0; i < size; i++ )
tb[i].interactable = interactable;
}
static register = function(parent = noone) {
for( var i = 0; i < size; i++ )
tb[i].register(parent);
}
static isHovering = function() {
for( var i = 0, n = array_length(tb); i < n; i++ ) if(tb[i].isHovering()) return true;
return false;
}
static apply = function() {
for( var i = 0; i < size; i++ ) {
tb[i].apply();
current_value[i] = toNumber(tb[i]._input_text);
}
}
static drawParam = function(params) {
setParam(params);
for(var i = 0; i < 4; i++) tb[i].setParam(params);
return draw(params.x, params.y, params.w, params.h, params.data, params.display_data, params.m);
}
static draw = function(_x, _y, _w, _h, _data, _display_data, _m) {
x = _x;
y = _y;
w = _w;
h = _h;
if(!is_array(_data)) return 0;
if(array_empty(_data)) return 0;
if(is_array(_data[0])) return 0;
var _bs = min(_h, ui(32));
var _disp = struct_try_get(_display_data, "angle_display");
if((_w - _bs) / 2 > ui(64)) {
var bx = _x + _w - _bs;
var by = _y + _h / 2 - _bs / 2;
tooltip.index = _disp;
if(buttonInstant(THEME.button_hide, bx, by, _bs, _bs, _m, iactive, ihover, tooltip, THEME.unit_angle, _disp, c_white) == 2) {
clickable = false;
_display_data.angle_display = (_disp + 1) % 2;
}
_w -= _bs + ui(8);
}
current_unit = _display_data.angle_display;
if(current_unit == QUARTERNION_DISPLAY.quarterion || (!tb[0].sliding && !tb[1].sliding && !tb[2].sliding)) {
current_value[0] = _data[0];
current_value[1] = _data[1];
current_value[2] = _data[2];
if(current_unit == QUARTERNION_DISPLAY.quarterion)
current_value[3] = _data[3];
}
size = _disp? 3 : 4;
var ww = _w / size;
var bx = _x;
disp_w = disp_w == noone? ww : lerp_float(disp_w, ww, 3);
var _dispDat = _data;
draw_sprite_stretched_ext(THEME.textbox, 3, _x, _y, _w, _h, c_white, 1);
draw_sprite_stretched_ext(THEME.textbox, 0, _x, _y, _w, _h, c_white, 0.5 + 0.5 * interactable);
for(var i = 0; i < size; i++) {
var _a = _dispDat[i];
tb[i].hide = true;
tb[i].setFocusHover(clickable && active, hover);
tb[i].draw(bx, _y, disp_w, _h, _a, _m);
bx += disp_w;
}
clickable = true;
resetFocus();
return _h;
}
static clone = function() { #region
var cln = new quarternionBox(onModify);
return cln;
} #endregion
}

View file

@ -0,0 +1,51 @@
// 2024-05-01 11:40:21
function shellOpenExplorer(path) { #region
if(OS == os_windows) {
var _windir = environment_get_variable("WINDIR") + "/explorer.exe";
path = string_replace_all(path, "/", "\\");
shell_execute_async(_windir, path);
} else if(OS == os_macosx) {
path = string_replace_all(path, "\\", "/");
var res = shell_execute_async("open", path);
}
} #endregion
function shell_execute(path, command, ref = noone) { #region
INLINE
if(OS == os_macosx) {
path = string_replace_all(path, "\\", "/");
command = string_replace_all(command, "\\", "/");
}
var txt = $"{path} {command}";
var res = ProcessExecute(txt);
print($"Execute {path} {command} | {res}");
return res;
} #endregion
function shell_execute_async(path, command, ref = noone, _log = true) { #region
INLINE
if(IS_CMD) return shell_execute(path, command, ref);
if(OS == os_macosx) {
path = string_replace_all(path, "\\", "/");
command = string_replace_all(command, "\\", "/");
}
var txt = $"{path} {command}";
var res = ProcessExecuteAsync(txt);
if(_log) print($"Execute async {path} {command} | {res}");
return res;
} #endregion
function env_user() { #region
INLINE
if(OS == os_windows) return string(environment_get_variable("userprofile")) + "\\AppData\\Local\\PixelComposer\\";
if(OS == os_macosx) return string(environment_get_variable("HOME")) + "/PixelComposer/";
return "";
} #endregion

View file

@ -1,4 +1,4 @@
// 2024-04-16 08:41:19 // 2024-05-01 11:31:11
function string_to_array(str) { #region function string_to_array(str) { #region
var amo = string_length(str); var amo = string_length(str);
var arr = array_create(amo); var arr = array_create(amo);
@ -66,12 +66,5 @@ function filename_name_only(name) { #region
return string_replace(name, filename_ext(name), "") return string_replace(name, filename_ext(name), "")
} #endregion } #endregion
function string_to_var(str) { #region function string_to_var(str) { INLINE return string_replace_all(string_lower(str), " ", "_"); }
INLINE function string_quote(str) { INLINE return $"\"{str}\""; }
return string_replace_all(string_lower(str), " ", "_");
} #endregion
function string_quote(str) { #region
INLINE
return $"\"{str}\"";
} #endregion

View file

@ -1,4 +1,4 @@
// 2024-04-16 08:29:49 // 2024-05-01 11:29:46
function string_to_array(str) { #region function string_to_array(str) { #region
var amo = string_length(str); var amo = string_length(str);
var arr = array_create(amo); var arr = array_create(amo);
@ -66,12 +66,5 @@ function filename_name_only(name) { #region
return string_replace(name, filename_ext(name), "") return string_replace(name, filename_ext(name), "")
} #endregion } #endregion
function string_to_var(str) { #region function string_to_var(str) { INLINE return string_replace_all(string_lower(str), " ", "_"); }
INLINE function string_quote(str) { INLINE return $"\"{str}\""; }
return string_replace_all(string_lower(str), " ", "_");
} #endregion
function string_quote(str) { #region
INLINE
return $"\"{str}\"";
} #endregion

View file

@ -1,4 +1,4 @@
// 2024-04-18 13:00:44 // 2024-05-01 11:57:36
enum TEXTBOX_INPUT { enum TEXTBOX_INPUT {
text, text,
number number
@ -7,9 +7,9 @@ enum TEXTBOX_INPUT {
function textBox(_input, _onModify) : textInput(_input, _onModify) constructor { function textBox(_input, _onModify) : textInput(_input, _onModify) constructor {
onRelease = noone; onRelease = noone;
align = _input == TEXTBOX_INPUT.number? fa_center : fa_left; align = _input == TEXTBOX_INPUT.number? fa_center : fa_left;
hide = false; hide = false;
color = COLORS._main_text; color = COLORS._main_text;
boxColor = c_white; boxColor = c_white;
format = TEXT_AREA_FORMAT._default; format = TEXT_AREA_FORMAT._default;
precision = 5; precision = 5;
@ -498,8 +498,10 @@ function textBox(_input, _onModify) : textInput(_input, _onModify) constructor {
draw_set_alpha(1); draw_set_alpha(1);
} }
} }
var _dpx = disp_x;
disp_x = lerp_float(disp_x, disp_x_to, 5); disp_x = lerp_float(disp_x, disp_x_to, 5);
if(_dpx != disp_x) _update = true;
var hoverRect = point_in_rectangle(_m[0], _m[1], _x, _y, _x + _w, _y + _h); var hoverRect = point_in_rectangle(_m[0], _m[1], _x, _y, _x + _w, _y + _h);

View file

@ -1,4 +1,4 @@
// 2024-04-18 13:00:36 // 2024-05-01 11:57:26
enum TEXTBOX_INPUT { enum TEXTBOX_INPUT {
text, text,
number number
@ -7,9 +7,9 @@ enum TEXTBOX_INPUT {
function textBox(_input, _onModify) : textInput(_input, _onModify) constructor { function textBox(_input, _onModify) : textInput(_input, _onModify) constructor {
onRelease = noone; onRelease = noone;
align = _input == TEXTBOX_INPUT.number? fa_center : fa_left; align = _input == TEXTBOX_INPUT.number? fa_center : fa_left;
hide = false; hide = false;
color = COLORS._main_text; color = COLORS._main_text;
boxColor = c_white; boxColor = c_white;
format = TEXT_AREA_FORMAT._default; format = TEXT_AREA_FORMAT._default;
precision = 5; precision = 5;
@ -498,8 +498,10 @@ function textBox(_input, _onModify) : textInput(_input, _onModify) constructor {
draw_set_alpha(1); draw_set_alpha(1);
} }
} }
var _dpx = disp_x;
disp_x = lerp_float(disp_x, disp_x_to, 5); disp_x = lerp_float(disp_x, disp_x_to, 5);
if(_dpx != disp_x) _update = true;
var hoverRect = point_in_rectangle(_m[0], _m[1], _x, _y, _x + _w, _y + _h); var hoverRect = point_in_rectangle(_m[0], _m[1], _x, _y, _x + _w, _y + _h);

View file

@ -551,6 +551,17 @@ event_inherited();
PREF_SAVE(); PREF_SAVE();
}) })
)); ));
ds_list_add(pref_node, new __Panel_Linear_Setting_Item_Preference(
__txtx("pref_node_3d_preview", "Preview surface size"),
"node_3d_preview_size",
new textBox(TEXTBOX_INPUT.number, function(val) {
PREFERENCES.node_3d_preview_size = clamp(val, 16, 1024);
PREF_SAVE();
})
));
#endregion #endregion
#region theme #region theme

View file

@ -2,110 +2,112 @@
if(winMan_isMinimized()) exit; if(winMan_isMinimized()) exit;
#region tooltip #region tooltip
if(is_struct(TOOLTIP)) { if(!_MOUSE_BLOCK) {
if(struct_has(TOOLTIP, "drawTooltip")) if(is_struct(TOOLTIP)) {
TOOLTIP.drawTooltip(); if(struct_has(TOOLTIP, "drawTooltip"))
TOOLTIP.drawTooltip();
} else if(is_array(TOOLTIP)) {
var content = TOOLTIP[0];
var type = TOOLTIP[1];
if(is_method(content)) content = content();
switch(type) {
case VALUE_TYPE.float :
case VALUE_TYPE.integer :
case VALUE_TYPE.text :
case VALUE_TYPE.struct :
case VALUE_TYPE.path :
draw_tooltip_text(string_real(content));
break;
case VALUE_TYPE.boolean : } else if(is_array(TOOLTIP)) {
draw_tooltip_text(printBool(content)); var content = TOOLTIP[0];
break; var type = TOOLTIP[1];
case VALUE_TYPE.curve : if(is_method(content)) content = content();
draw_tooltip_text("[" + __txt("Curve Object") + "]");
break;
case VALUE_TYPE.color : switch(type) {
draw_tooltip_color(content); case VALUE_TYPE.float :
break; case VALUE_TYPE.integer :
case VALUE_TYPE.text :
case VALUE_TYPE.gradient : case VALUE_TYPE.struct :
draw_tooltip_gradient(content); case VALUE_TYPE.path :
break; draw_tooltip_text(string_real(content));
break;
case VALUE_TYPE.d3object :
draw_tooltip_text("[" + __txt("3D Object") + "]"); case VALUE_TYPE.boolean :
break; draw_tooltip_text(printBool(content));
break;
case VALUE_TYPE.object :
draw_tooltip_text("[" + __txt("Object") + "]"); case VALUE_TYPE.curve :
break; draw_tooltip_text("[" + __txt("Curve Object") + "]");
break;
case VALUE_TYPE.surface :
draw_tooltip_surface(content); case VALUE_TYPE.color :
break; draw_tooltip_color(content);
break;
case VALUE_TYPE.rigid :
draw_tooltip_text("[" + __txt("Rigidbody Object") + " (id: " + string(content[$ "object"]) + ")]"); case VALUE_TYPE.gradient :
break; draw_tooltip_gradient(content);
break;
case VALUE_TYPE.particle :
var txt = "[" + case VALUE_TYPE.d3object :
__txt("Particle Object") + draw_tooltip_text("[" + __txt("3D Object") + "]");
" (size: " + string(array_length(content)) + ") " + break;
"]";
draw_tooltip_text(txt); case VALUE_TYPE.object :
break; draw_tooltip_text("[" + __txt("Object") + "]");
break;
case VALUE_TYPE.pathnode :
draw_tooltip_text("[" + __txt("Path Object") + "]"); case VALUE_TYPE.surface :
break; draw_tooltip_surface(content);
break;
case VALUE_TYPE.sdomain :
draw_tooltip_text("[" + __txt("Domain") + " (id: " + string(content) + ")]"); case VALUE_TYPE.rigid :
break; draw_tooltip_text("[" + __txt("Rigidbody Object") + " (id: " + string(content[$ "object"]) + ")]");
break;
case VALUE_TYPE.strands :
var txt = __txt("Strands Object"); case VALUE_TYPE.particle :
if(is_struct(content)) var txt = "[" +
txt += " (strands: " + string(array_length(content.hairs)) + ")"; __txt("Particle Object") +
draw_tooltip_text("[" + txt + "]"); " (size: " + string(array_length(content)) + ") " +
break; "]";
draw_tooltip_text(txt);
case VALUE_TYPE.mesh : break;
var txt = __txt("Mesh Object");
if(is_struct(content)) case VALUE_TYPE.pathnode :
txt += " (triangles: " + string(array_length(content.triangles)) + ")"; draw_tooltip_text("[" + __txt("Path Object") + "]");
draw_tooltip_text("[" + txt + "]"); break;
break;
case VALUE_TYPE.sdomain :
case VALUE_TYPE.d3vertex : draw_tooltip_text("[" + __txt("Domain") + " (id: " + string(content) + ")]");
var txt = __txt("3D Vertex"); break;
txt += " (groups: " + string(array_length(content)) + ")";
draw_tooltip_text("[" + txt + "]"); case VALUE_TYPE.strands :
break; var txt = __txt("Strands Object");
if(is_struct(content))
case VALUE_TYPE.buffer : txt += " (strands: " + string(array_length(content.hairs)) + ")";
draw_tooltip_buffer(content); draw_tooltip_text("[" + txt + "]");
break; break;
case "sprite" : case VALUE_TYPE.mesh :
draw_tooltip_sprite(content); var txt = __txt("Mesh Object");
break; if(is_struct(content))
txt += " (triangles: " + string(array_length(content.triangles)) + ")";
default : draw_tooltip_text("[" + txt + "]");
var tt = ""; break;
if(is_struct(content)) tt = $"[{instanceof(content)}] {content}";
else tt = string(content); case VALUE_TYPE.d3vertex :
var txt = __txt("3D Vertex");
draw_tooltip_text(tt); txt += " (groups: " + string(array_length(content)) + ")";
} draw_tooltip_text("[" + txt + "]");
} else if(TOOLTIP != "") break;
draw_tooltip_text(TOOLTIP);
case VALUE_TYPE.buffer :
draw_tooltip_buffer(content);
break;
case "sprite" :
draw_tooltip_sprite(content);
break;
default :
var tt = "";
if(is_struct(content)) tt = $"[{instanceof(content)}] {content}";
else tt = string(content);
draw_tooltip_text(tt);
}
} else if(TOOLTIP != "")
draw_tooltip_text(TOOLTIP);
}
TOOLTIP = ""; TOOLTIP = "";
#endregion #endregion

View file

@ -587,7 +587,7 @@ function BBMOD_Quaternion(_x=0.0, _y=0.0, _z=0.0, _w=1.0) constructor
return self; return self;
}; };
static ToEuler = function() { static ToEuler = function(isArray = false) {
var ysqr = Y * Y; var ysqr = Y * Y;
// roll (x-axis rotation) // roll (x-axis rotation)
@ -606,11 +606,15 @@ function BBMOD_Quaternion(_x=0.0, _y=0.0, _z=0.0, _w=1.0) constructor
var yaw = arctan2(t3, t4); var yaw = arctan2(t3, t4);
// Convert radians to degrees // Convert radians to degrees
var _dx = roll * 180.0 / pi; var _dx = roll * 180.0 / pi;
var _dy = pitch * 180.0 / pi; var _dy = pitch * 180.0 / pi;
var _dz = yaw * 180.0 / pi; var _dz = yaw * 180.0 / pi;
return new __rot3(_dx, _dy, _dz); _dx = round(_dx * 1000) / 1000;
_dy = round(_dy * 1000) / 1000;
_dz = round(_dz * 1000) / 1000;
return isArray? [ _dx, _dy, _dz ] : new __rot3(_dx, _dy, _dz);
} }
/// @func ToMatrix([_dest[, _index]]) /// @func ToMatrix([_dest[, _index]])

View file

@ -35,7 +35,7 @@ function Node_3D(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) constr
static refreshPreview = function() { #region static refreshPreview = function() { #region
var _prev_obj = getPreviewObjects(); var _prev_obj = getPreviewObjects();
mesh_prev_surface = surface_verify(mesh_prev_surface, 64, 64); mesh_prev_surface = surface_verify(mesh_prev_surface, PREFERENCES.node_3d_preview_size, PREFERENCES.node_3d_preview_size);
surface_set_target(mesh_prev_surface); surface_set_target(mesh_prev_surface);
DRAW_CLEAR DRAW_CLEAR
@ -56,12 +56,12 @@ function Node_3D(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) constr
D3D_GLOBAL_PREVIEW.custom_transform.position.set(_c._multiply(-1)); D3D_GLOBAL_PREVIEW.custom_transform.position.set(_c._multiply(-1));
var _sca = 1 / _b.getMaximumScale(); var _sca = 2 / _b.getScale();
D3D_GLOBAL_PREVIEW.custom_transform.scale.set(_sca); //print($"Submitting object {_prev}\n{_b}, {_c}, {_sca}");
D3D_GLOBAL_PREVIEW.custom_transform.scale.set(_sca);
D3D_GLOBAL_PREVIEW.submitUI(_prev); D3D_GLOBAL_PREVIEW.submitUI(_prev);
} }
surface_reset_target(); surface_reset_target();
D3D_GLOBAL_PREVIEW.camera.resetCamera(); D3D_GLOBAL_PREVIEW.camera.resetCamera();
@ -78,5 +78,8 @@ function Node_3D(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) constr
if(!isHighlightingInGraph()) aa *= 0.25; if(!isHighlightingInGraph()) aa *= 0.25;
draw_surface_bbox(mesh_prev_surface, bbox,, aa); draw_surface_bbox(mesh_prev_surface, bbox,, aa);
onDrawNodeOver(xx, yy, _mx, _my, _s, _hover, _focus);
} #endregion } #endregion
static onDrawNodeOver = function(xx, yy, _mx, _my, _s, _hover = false, _focus = false) { }
} }

View file

@ -49,9 +49,9 @@ function Node_3D_Object(_x, _y, _group = noone) : Node_3D(_x, _y, _group) constr
tools = [ tool_pos, tool_rot, tool_sca ]; tools = [ tool_pos, tool_rot, tool_sca ];
tool_axis_edit = new scrollBox([ "local", "global" ], function(val) { tool_attribute.context = val; }); tool_axis_edit = new scrollBox([ "local", "global" ], function(val) { tool_attribute.context = val; });
tool_axis_edit.font = f_p2; // tool_axis_edit.font = f_p2;
tool_axis_edit.arrow_spr = THEME.arrow; // tool_axis_edit.arrow_spr = THEME.arrow;
tool_axis_edit.arrow_ind = 3; // tool_axis_edit.arrow_ind = 3;
tool_attribute.context = 0; tool_attribute.context = 0;
tool_settings = [ tool_settings = [
[ "Axis", tool_axis_edit, "context", tool_attribute ], [ "Axis", tool_axis_edit, "context", tool_attribute ],
@ -279,7 +279,7 @@ function Node_3D_Object(_x, _y, _group = noone) : Node_3D(_x, _y, _group) constr
static drawGizmoRotation = function(index, object, _vpos, active, params, _mx, _my, _snx, _sny, _panel) { #region static drawGizmoRotation = function(index, object, _vpos, active, params, _mx, _my, _snx, _sny, _panel) { #region
#region ---- main ---- #region ---- main ----
var _rot = inputs[| index].getValue(,,, true); var _rot = inputs[| index].getValue();
var _qrot = object == noone? new BBMOD_Quaternion() : object.transform.rotation; var _qrot = object == noone? new BBMOD_Quaternion() : object.transform.rotation;
var _qinv = new BBMOD_Quaternion().FromAxisAngle(new BBMOD_Vec3(1, 0, 0), 90); var _qinv = new BBMOD_Quaternion().FromAxisAngle(new BBMOD_Vec3(1, 0, 0), 90);
@ -293,7 +293,7 @@ function Node_3D_Object(_x, _y, _group = noone) : Node_3D(_x, _y, _group) constr
var th; var th;
var _posView = _camera.worldPointToViewPoint(_vpos); var _posView = _camera.worldPointToViewPoint(_vpos);
var cx = _posView.x; var cx = _posView.x;
var cy = _posView.y; var cy = _posView.y;

View file

@ -19,4 +19,4 @@ function __bbox3D(first, second) constructor {
abs(first.z - second.z), abs(first.z - second.z),
); );
} }
} }

View file

@ -17,7 +17,7 @@ function __3dObject() constructor {
vertex = []; vertex = [];
normal_vertex = []; normal_vertex = [];
object_counts = 1; object_counts = 1;
VB = noone; VB = [];
NVB = noone; NVB = noone;
normal_draw_size = 0.2; normal_draw_size = 0.2;
@ -134,11 +134,9 @@ function __3dObject() constructor {
preSubmitVertex(scene); preSubmitVertex(scene);
if(VB != noone) { #region transform.submitMatrix();
transform.submitMatrix();
matrix_set(matrix_world, matrix_stack_top());
matrix_set(matrix_world, matrix_stack_top());
} #endregion
#region ++++ Submit & Material ++++ #region ++++ Submit & Material ++++
gpu_set_tex_repeat(true); gpu_set_tex_repeat(true);
@ -173,7 +171,7 @@ function __3dObject() constructor {
} else } else
vertex_submit(VB[i], render_type, _tex); vertex_submit(VB[i], render_type, _tex);
//print($"Submit vertex ({scene}) [{VB[i]}]"); // print($"Submit vertex ({scene}) [{VB[i]}: {vertex_get_buffer_size(VB[i])}]");
} }
gpu_set_tex_repeat(false); gpu_set_tex_repeat(false);
@ -242,11 +240,9 @@ function __3dObject() constructor {
} #endregion } #endregion
static destroy = function() { #region static destroy = function() { #region
if(is_array(VB)) { for( var i = 0, n = array_length(VB); i < n; i++ )
for( var i = 0, n = array_length(VB); i < n; i++ ) vertex_delete_buffer(VB[i]);
vertex_delete_buffer(VB[i]); VB = [];
} else if(VB != noone)
vertex_delete_buffer(VB);
onDestroy(); onDestroy();
} #endregion } #endregion

View file

@ -32,10 +32,13 @@ function Node_3D_Mesh_Obj(_x, _y, _group = noone) : Node_3D_Mesh(_x, _y, _group)
inputs[| in_mesh + 1] = nodeValue("Flip UV", self, JUNCTION_CONNECT.input, VALUE_TYPE.boolean, true, "Flip UV axis, can be use to fix some texture mapping error.") inputs[| in_mesh + 1] = nodeValue("Flip UV", self, JUNCTION_CONNECT.input, VALUE_TYPE.boolean, true, "Flip UV axis, can be use to fix some texture mapping error.")
.rejectArray(); .rejectArray();
inputs[| in_mesh + 2] = nodeValue("Import Scale", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 1)
.rejectArray();
input_display_list = [ input_display_list = [
__d3d_input_list_mesh, __d3d_input_list_mesh,
__d3d_input_list_transform, __d3d_input_list_transform,
["Object", false], in_mesh + 0, ["Object", false], in_mesh + 0, in_mesh + 2,
["Material", false], in_mesh + 1, ["Material", false], in_mesh + 1,
] ]
@ -57,7 +60,10 @@ function Node_3D_Mesh_Obj(_x, _y, _group = noone) : Node_3D_Mesh(_x, _y, _group)
insp1UpdateTooltip = __txt("Refresh"); insp1UpdateTooltip = __txt("Refresh");
insp1UpdateIcon = [ THEME.refresh_icon, 1, COLORS._main_value_positive ]; insp1UpdateIcon = [ THEME.refresh_icon, 1, COLORS._main_value_positive ];
static onInspector1Update = function() { current_path = ""; } static onInspector1Update = function() {
current_path = "";
outputs[| 0].setValue(noone);
}
function setPath(path) { inputs[| in_mesh + 0].setValue(path); } function setPath(path) { inputs[| in_mesh + 0].setValue(path); }
@ -100,7 +106,9 @@ function Node_3D_Mesh_Obj(_x, _y, _group = noone) : Node_3D_Mesh(_x, _y, _group)
if(!file_exists_empty(_path)) return; if(!file_exists_empty(_path)) return;
current_path = _path; current_path = _path;
readObj_init(); var _scale = inputs[| in_mesh + 2].getValue();
readObj_init(_scale);
obj_read_time = get_timer(); obj_read_time = get_timer();
obj_read_file = file_text_open_read(current_path); obj_read_file = file_text_open_read(current_path);
@ -119,23 +127,24 @@ function Node_3D_Mesh_Obj(_x, _y, _group = noone) : Node_3D_Mesh(_x, _y, _group)
use_display_list = true; use_display_list = true;
if(obj_raw == noone) return; if(obj_raw == noone) return;
//var txt = $"========== OBJ import ==========\n"; // var txt = $"========== OBJ import ==========\n";
//txt += $"Vertex counts: {obj_raw.vertex_count}\n"; // txt += $"Vertex counts: {obj_raw.vertex_count}\n";
//txt += $"Object counts: {obj_raw.object_counts}\n"; // txt += $"Object counts: {obj_raw.object_counts}\n";
//txt += $"Material counts: {array_length(obj_raw.materials)}\n"; // txt += $"Material counts: {array_length(obj_raw.materials)}\n";
//txt += $"Model BBOX: {obj_raw.model_size}\n"; // txt += $"Model BBOX: {obj_raw.model_size}\n";
//txt += $"Load completed in {(get_timer() - obj_read_time) / 1000} ms\n"; // txt += $"Load completed in {(get_timer() - obj_read_time) / 1000} ms\n";
//print(txt); // print(txt);
var span = max(abs(obj_raw.model_size.x), abs(obj_raw.model_size.y), abs(obj_raw.model_size.z)); var span = max(abs(obj_raw.model_size.x), abs(obj_raw.model_size.y), abs(obj_raw.model_size.z));
if(span > 10) noti_warning($"The model is tool large to display properly ({span}u). Scale the model down to preview."); if(span > 10) noti_warning($"The model is tool large to display properly ({span}u). Scale the model down to preview.");
if(object != noone) object.destroy(); if(object != noone) object.destroy();
object = new __3dObject(); object = new __3dObject();
object.VB = obj_raw.vertex_groups; object.VB = obj_raw.vertex_groups;
object.vertex = obj_raw.vertex; object.vertex = obj_raw.vertex;
object.object_counts = obj_raw.object_counts;
object.size = obj_raw.model_size; object.size = obj_raw.model_size;
object.object_counts = obj_raw.object_counts;
use_normal = obj_raw.use_normal; use_normal = obj_raw.use_normal;
materialNames = [ "Material" ]; materialNames = [ "Material" ];
@ -171,6 +180,8 @@ function Node_3D_Mesh_Obj(_x, _y, _group = noone) : Node_3D_Mesh(_x, _y, _group)
for(var i = 0; i < array_length(materialNames); i++) for(var i = 0; i < array_length(materialNames); i++)
createMaterial(i); createMaterial(i);
outputs[| 0].setValue(object);
triggerRender(); triggerRender();
} #endregion } #endregion
@ -206,6 +217,7 @@ function Node_3D_Mesh_Obj(_x, _y, _group = noone) : Node_3D_Mesh(_x, _y, _group)
_object.VB = object.VB; _object.VB = object.VB;
_object.NVB = object.NVB; _object.NVB = object.NVB;
_object.vertex = object.vertex; _object.vertex = object.vertex;
_object.size = object.size;
_object.object_counts = object.object_counts; _object.object_counts = object.object_counts;
_object.materials = materials; _object.materials = materials;
_object.material_index = materialIndex; _object.material_index = materialIndex;
@ -217,7 +229,7 @@ function Node_3D_Mesh_Obj(_x, _y, _group = noone) : Node_3D_Mesh(_x, _y, _group)
static getPreviewValues = function() { return array_safe_get_fast(all_inputs, in_mesh + 2, noone); } static getPreviewValues = function() { return array_safe_get_fast(all_inputs, in_mesh + 2, noone); }
static onDrawNode = function(xx, yy, _mx, _my, _s, _hover, _focus) { #region static onDrawNodeOver = function(xx, yy, _mx, _my, _s, _hover, _focus) { #region
if(!obj_reading) return; if(!obj_reading) return;
var cx = xx + w * _s / 2; var cx = xx + w * _s / 2;
@ -225,7 +237,6 @@ function Node_3D_Mesh_Obj(_x, _y, _group = noone) : Node_3D_Mesh(_x, _y, _group)
var rr = min(w - 32, h - 32) * _s / 2; var rr = min(w - 32, h - 32) * _s / 2;
draw_set_color(COLORS._main_icon); draw_set_color(COLORS._main_icon);
//draw_arc(cx, cy, rr, 90, 90 - 360 * (obj_read_progress + obj_read_prog_sub) / obj_read_prog_tot, 4 * _s, 180);
draw_arc(cx, cy, rr, current_time / 5, current_time / 5 + 90, 4 * _s, 90); draw_arc(cx, cy, rr, current_time / 5, current_time / 5 + 90, 4 * _s, 90);
} #endregion } #endregion
} }

View file

@ -302,7 +302,7 @@ function Node_Export(_x, _y, _group = noone) : Node(_x, _y, _group) constructor
} #endregion } #endregion
static renderWebp = function(temp_path, target_path) { #region static renderWebp = function(temp_path, target_path) { #region
var _path = file_find_first(temp_path + "*.png", 0); var _path = file_find_first(temp_path + "*.png", 0);
var frames = []; var frames = [];
while(_path != "") { while(_path != "") {
@ -311,7 +311,7 @@ function Node_Export(_x, _y, _group = noone) : Node(_x, _y, _group) constructor
var shell_cmd = _pathTemp + " -define webp:lossless=true " + _frame; var shell_cmd = _pathTemp + " -define webp:lossless=true " + _frame;
array_push(frames, _frame); array_push(frames, _frame);
shell_execute_async(magick, shell_cmd, self); shell_execute_async(magick, shell_cmd, self, false);
_path = file_find_next(); _path = file_find_next();
} }
@ -323,7 +323,7 @@ function Node_Export(_x, _y, _group = noone) : Node(_x, _y, _group) constructor
var cmd = ""; var cmd = "";
for( var i = 0, n = array_length(frames); i < n; i++ ) for( var i = 0, n = array_length(frames); i < n; i++ )
cmd += "-frame " + frames[i] + " +" + string(framerate) + "+0+0+1 "; cmd += $"-frame {frames[i]} +{framerate}+0+0+1 ";
cmd += "-bgcolor 0,0,0,0 "; cmd += "-bgcolor 0,0,0,0 ";
cmd += "-o " + string_quote(target_path); cmd += "-o " + string_quote(target_path);
@ -341,7 +341,9 @@ function Node_Export(_x, _y, _group = noone) : Node(_x, _y, _group) constructor
var qual = getInputData(10); var qual = getInputData(10);
if(rate == 0) rate = 1; if(rate == 0) rate = 1;
temp_path = string_replace_all(temp_path, "/", "\\");
target_path = string_replace_all(target_path, "/", "\\"); target_path = string_replace_all(target_path, "/", "\\");
var framerate = 100 / rate; var framerate = 100 / rate;
var loop_str = loop? 0 : 1; var loop_str = loop? 0 : 1;
var use_gifski = false; var use_gifski = false;

View file

@ -13,7 +13,7 @@ function Node_Grid(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) cons
.setMappable(13); .setMappable(13);
inputs[| 3] = nodeValue("Gap", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0.1) inputs[| 3] = nodeValue("Gap", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0.1)
.setDisplay(VALUE_DISPLAY.slider, { range: [0, 0.5, 0.01] }) .setDisplay(VALUE_DISPLAY.slider, { range: [0, 0.5, 0.001] })
.setMappable(14); .setMappable(14);
inputs[| 4] = nodeValue("Angle", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0) inputs[| 4] = nodeValue("Angle", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0)

View file

@ -17,7 +17,7 @@ function Node_Grid_Hex(_x, _y, _group = noone) : Node_Processor(_x, _y, _group)
.setMappable(12); .setMappable(12);
inputs[| 4] = nodeValue("Gap", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0.1) inputs[| 4] = nodeValue("Gap", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0.1)
.setDisplay(VALUE_DISPLAY.slider, { range: [0, 0.5, 0.01] }) .setDisplay(VALUE_DISPLAY.slider, { range: [0, 0.5, 0.001] })
.setMappable(13); .setMappable(13);
inputs[| 5] = nodeValue("Tile color", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white) ) inputs[| 5] = nodeValue("Tile color", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white) )

View file

@ -13,7 +13,7 @@ function Node_Grid_Tri(_x, _y, _group = noone) : Node_Processor(_x, _y, _group)
.setMappable(11); .setMappable(11);
inputs[| 3] = nodeValue("Gap", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0.1) inputs[| 3] = nodeValue("Gap", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0.1)
.setDisplay(VALUE_DISPLAY.slider, { range: [0, 0.5, 0.01] }) .setDisplay(VALUE_DISPLAY.slider, { range: [0, 0.5, 0.001] })
.setMappable(12); .setMappable(12);
inputs[| 4] = nodeValue("Angle", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0) inputs[| 4] = nodeValue("Angle", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0)

View file

@ -17,7 +17,7 @@ function Node_Herringbone_Tile(_x, _y, _group = noone) : Node_Processor(_x, _y,
.setMappable(12); .setMappable(12);
inputs[| 4] = nodeValue("Gap", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0.25) inputs[| 4] = nodeValue("Gap", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0.25)
.setDisplay(VALUE_DISPLAY.slider, { range: [0, 0.5, 0.01] }) .setDisplay(VALUE_DISPLAY.slider, { range: [0, 0.5, 0.001] })
.setMappable(13); .setMappable(13);
inputs[| 5] = nodeValue("Tile color", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white) ) inputs[| 5] = nodeValue("Tile color", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white) )

View file

@ -17,7 +17,7 @@ function Node_Pytagorean_Tile(_x, _y, _group = noone) : Node_Processor(_x, _y, _
.setMappable(12); .setMappable(12);
inputs[| 4] = nodeValue("Gap", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0.25) inputs[| 4] = nodeValue("Gap", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0.25)
.setDisplay(VALUE_DISPLAY.slider, { range: [0, 0.5, 0.01] }) .setDisplay(VALUE_DISPLAY.slider, { range: [0, 0.5, 0.001] })
.setMappable(13); .setMappable(13);
inputs[| 5] = nodeValue("Tile color", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white) ) inputs[| 5] = nodeValue("Tile color", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white) )

View file

@ -17,7 +17,7 @@ function Node_Random_Tile(_x, _y, _group = noone) : Node_Processor(_x, _y, _grou
.setMappable(12); .setMappable(12);
inputs[| 4] = nodeValue("Gap", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0.1) inputs[| 4] = nodeValue("Gap", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0.1)
.setDisplay(VALUE_DISPLAY.slider, { range: [0, 0.5, 0.01] }) .setDisplay(VALUE_DISPLAY.slider, { range: [0, 0.5, 0.001] })
.setMappable(13); .setMappable(13);
inputs[| 5] = nodeValue("Tile color", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white) ) inputs[| 5] = nodeValue("Tile color", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white) )

View file

@ -59,7 +59,7 @@ function Node_Shadow_Cast(_x, _y, _group = noone) : Node_Processor(_x, _y, _grou
.setDisplay(VALUE_DISPLAY.slider, { range: [0, 16, 0.1] }); .setDisplay(VALUE_DISPLAY.slider, { range: [0, 16, 0.1] });
inputs[| 16] = nodeValue("Ambient occlusion strength", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0.1) inputs[| 16] = nodeValue("Ambient occlusion strength", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0.1)
.setDisplay(VALUE_DISPLAY.slider, { range: [0, 0.5, 0.01] }); .setDisplay(VALUE_DISPLAY.slider, { range: [0, 0.5, 0.001] });
inputs[| 17] = nodeValue("Active", self, JUNCTION_CONNECT.input, VALUE_TYPE.boolean, true); inputs[| 17] = nodeValue("Active", self, JUNCTION_CONNECT.input, VALUE_TYPE.boolean, true);
active_index = 17; active_index = 17;

View file

@ -64,7 +64,7 @@ function Node_Shape(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) con
.setDisplay(VALUE_DISPLAY.rotation_range); .setDisplay(VALUE_DISPLAY.rotation_range);
inputs[| 9] = nodeValue("Corner radius", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0) inputs[| 9] = nodeValue("Corner radius", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0)
.setDisplay(VALUE_DISPLAY.slider, { range: [0, 0.5, 0.01] }); .setDisplay(VALUE_DISPLAY.slider, { range: [0, 0.5, 0.001] });
inputs[| 10] = nodeValue("Shape color", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, c_white); inputs[| 10] = nodeValue("Shape color", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, c_white);

View file

@ -1151,7 +1151,7 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
}); });
extract_node = "Node_Vector4"; extract_node = "Node_Vector4";
display_data.angle_display = QUARTERNION_DISPLAY.quarterion; display_data.angle_display = QUARTERNION_DISPLAY.euler;
break; #endregion break; #endregion
case VALUE_DISPLAY.path_anchor : #region case VALUE_DISPLAY.path_anchor : #region
@ -1551,19 +1551,6 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
return applyUnit? unit.apply(value, arrIndex) : value; return applyUnit? unit.apply(value, arrIndex) : value;
} #endregion } #endregion
if(display_type == VALUE_DISPLAY.d3quarternion) { #region
if(!applyUnit) return value;
var dispType = display_data.angle_display;
switch(dispType) {
case QUARTERNION_DISPLAY.quarterion :
return value;
case QUARTERNION_DISPLAY.euler :
var euler = new BBMOD_Quaternion().FromEuler(value[0], value[1], value[2]).ToArray();
return euler;
}
} #endregion
if(type == VALUE_TYPE.text) { #region if(type == VALUE_TYPE.text) { #region
switch(display_type) { switch(display_type) {
case VALUE_DISPLAY.text_array : return value; case VALUE_DISPLAY.text_array : return value;
@ -1910,6 +1897,13 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
} else } else
val = ds_list_empty(animator.values)? 0 : animator.processType(animator.values[| 0].value); val = ds_list_empty(animator.values)? 0 : animator.processType(animator.values[| 0].value);
if(display_type == VALUE_DISPLAY.d3quarternion) {
switch(display_data.angle_display) {
case QUARTERNION_DISPLAY.quarterion : return val;
case QUARTERNION_DISPLAY.euler : return new BBMOD_Quaternion(val[0], val[1], val[2], val[3]).ToEuler(true);
}
}
return val; return val;
} #endregion } #endregion
@ -1992,6 +1986,7 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
static setValueInspector = function(val = 0, index = noone) { #region static setValueInspector = function(val = 0, index = noone) { #region
INLINE INLINE
var res = false; var res = false;
val = unit.invApply(val); val = unit.invApply(val);
@ -2002,27 +1997,32 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
var _node = PANEL_INSPECTOR.inspectings[i]; var _node = PANEL_INSPECTOR.inspectings[i];
if(ind >= ds_list_size(_node.inputs)) continue; if(ind >= ds_list_size(_node.inputs)) continue;
var r = _node.inputs[| ind].setValueDirect(val, index); var r = _node.inputs[| ind].setValueInspectorDirect(val, index);
if(_node == node) res = r; if(_node == node) res = r;
} }
} else { } else {
res = setValueDirect(val, index); res = setValueInspectorDirect(val, index);
//print($"Node {node} : {node.name} {node.internalName}");
//print($"Inspecting {PANEL_INSPECTOR.inspecting} : {PANEL_INSPECTOR.inspecting.name} {PANEL_INSPECTOR.inspecting.internalName}");
//print($"{node == PANEL_INSPECTOR.inspecting}");
//print("");
} }
return res; return res;
} #endregion } #endregion
static setValueInspectorDirect = function(val = 0, index = noone) {
if(display_type == VALUE_DISPLAY.d3quarternion && display_data.angle_display == QUARTERNION_DISPLAY.euler) {
var _qval = new BBMOD_Quaternion().FromEuler(-val[0], -val[1], -val[2]).ToArray();
var _eval = new BBMOD_Quaternion(_qval[0], _qval[1], _qval[2], _qval[3]).ToEuler(true);
return setValueDirect(_qval);
}
return setValueDirect(val, index);
}
static setValueDirect = function(val = 0, index = noone, record = true, time = CURRENT_FRAME, _update = true) { #region static setValueDirect = function(val = 0, index = noone, record = true, time = CURRENT_FRAME, _update = true) { #region
is_modified = true; is_modified = true;
var updated = false; var updated = false;
var _val = val; var _val = val;
var _inp = connect_type == JUNCTION_CONNECT.input; var _inp = connect_type == JUNCTION_CONNECT.input;
if(sep_axis) { if(sep_axis) {
if(index == noone) { if(index == noone) {
for( var i = 0, n = array_length(animators); i < n; i++ ) for( var i = 0, n = array_length(animators); i < n; i++ )
@ -2038,8 +2038,7 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
_val[index] = val; _val[index] = val;
} }
updated = animator.setValue(_val, _inp && record, time); updated = animator.setValue(_val, _inp && record, time);
//print($"{updated}: {index} - {_val}");
} }
if(type == VALUE_TYPE.gradient) updated = true; if(type == VALUE_TYPE.gradient) updated = true;

View file

@ -1,18 +1,22 @@
function readObj_init() { function readObj_init(_scale = 1) {
obj_reading = true; obj_reading = true;
obj_read_progress = 0; obj_read_progress = 0;
obj_read_prog_sub = 0; obj_read_prog_sub = 0;
obj_read_prog_tot = 3; obj_read_prog_tot = 3;
obj_raw = noone; obj_raw = noone;
obj_reading_scale = _scale;
_VB = []; _VB = [];
_VBT = []; _VBT = [];
_VBN = []; _VBN = [];
mats = []; mats = [];
matIndex = []; matIndex = [];
tris = []; tris = [];
mtlPath = ""; mtlPath = "";
use_normal = true; use_normal = true;
v = ds_list_create(); v = ds_list_create();
vt = ds_list_create(); vt = ds_list_create();
vn = ds_list_create(); vn = ds_list_create();
@ -34,7 +38,11 @@ function readObj_file() {
switch(sep[0]) { switch(sep[0]) {
case "v" : case "v" :
ds_list_add(v, [ toNumber(sep[1]), toNumber(sep[2]), toNumber(sep[3]) ]); ds_list_add(v, [
toNumber(sep[1]) * obj_reading_scale,
toNumber(sep[2]) * obj_reading_scale,
toNumber(sep[3]) * obj_reading_scale
]);
break; break;
case "vt" : case "vt" :
@ -43,15 +51,11 @@ function readObj_file() {
ds_list_add(vt, [ _u, _v ]); ds_list_add(vt, [ _u, _v ]);
break; break;
case "vn" : case "vn" :
var _nx = toNumber(sep[1]); var _nx = toNumber(sep[1]);
var _ny = toNumber(sep[2]); var _ny = toNumber(sep[2]);
var _nz = toNumber(sep[3]); var _nz = toNumber(sep[3]);
//var _di = sqrt(_nx * _nx + _ny * _ny + _nz * _nz);
//_nx /= _di;
//_ny /= _di;
//_nz /= _di;
ds_list_add(vn, [ _nx, _ny, _nz ]); ds_list_add(vn, [ _nx, _ny, _nz ]);
break; break;
@ -143,23 +147,28 @@ function readObj_cent() {
_bmin = v[| 0]; _bmin = v[| 0];
_bmax = v[| 0]; _bmax = v[| 0];
cv = [0, 0, 0]; cv = [0, 0, 0];
vertex = ds_list_size(v); vertex = ds_list_size(v);
for( var i = 0; i < vertex; i++ ) { for( var i = 0; i < vertex; i++ ) {
var _v = v[| i]; var _v = v[| i];
cv[0] += _v[0]; var _v0 = _v[0];
cv[1] += _v[1]; var _v1 = _v[1];
cv[2] += _v[2]; var _v2 = _v[2];
cv[0] += _v0;
cv[1] += _v1;
cv[2] += _v2;
_bmin = [ _bmin = [
min(_bmin[0], _v[0]), min(_bmin[0], _v0),
min(_bmin[1], _v[1]), min(_bmin[1], _v1),
min(_bmin[2], _v[2]), min(_bmin[2], _v2),
]; ];
_bmax = [ _bmax = [
max(_bmax[0], _v[0]), max(_bmax[0], _v0),
max(_bmax[1], _v[1]), max(_bmax[1], _v1),
max(_bmax[2], _v[2]), max(_bmax[2], _v2),
]; ];
} }
@ -173,7 +182,9 @@ function readObj_cent() {
_bmax[2] - _bmin[2], _bmax[2] - _bmin[2],
); );
var sc = 1; //print($"{obj_size}");
var sc = 1;
//var span = max(abs(_size.x), abs(_size.y), abs(_size.z)); //var span = max(abs(_size.x), abs(_size.y), abs(_size.z));
//if(span > 10) sc = span / 10; //if(span > 10) sc = span / 10;

View file

@ -166,7 +166,7 @@ function Panel_Graph(project = PROJECT) : PanelContent() constructor {
#endregion #endregion
#region ---- position ---- #region ---- position ----
scale = [ 0.01, 0.02, 0.05, 0.10, 0.15, 0.20, 0.25, 0.33, 0.50, 0.65, 0.80, 1, 1.2, 1.35, 1.5, 2.0]; scale = [ 0.01, 0.02, 0.05, 0.10, 0.15, 0.20, 0.25, 0.33, 0.50, 0.65, 0.80, 1, 1.2, 1.35, 1.5, 2.0 ];
graph_s = 1; graph_s = 1;
graph_s_to = graph_s; graph_s_to = graph_s;

View file

@ -926,7 +926,7 @@ function Panel_Preview() : PanelContent() constructor {
#region defer #region defer
var _prev_obj = _prev_node.getPreviewObject(); var _prev_obj = _prev_node.getPreviewObject();
d3_deferData = d3_scene_preview.deferPass(_prev_obj, w, h, d3_deferData); if(_prev_obj) d3_deferData = d3_scene_preview.deferPass(_prev_obj, w, h, d3_deferData);
#endregion #endregion
#region grid #region grid
@ -1503,6 +1503,7 @@ function Panel_Preview() : PanelContent() constructor {
} }
wdg.setFocusHover(pFOCUS, pHOVER); wdg.setFocusHover(pFOCUS, pHOVER);
var _tool_font = f_p3;
switch(instanceof(wdg)) { switch(instanceof(wdg)) {
case "textBox" : case "textBox" :
@ -1511,16 +1512,27 @@ function Panel_Preview() : PanelContent() constructor {
break; break;
case "buttonGroup": case "buttonGroup":
case "checkBoxGroup" : tolw = tolh * wdg.size; break; case "checkBoxGroup" :
tolw = tolh * wdg.size;
break;
case "checkBox" : tolw = tolh; break; case "checkBox" :
case "scrollBox" : tolw = ui(96); break; tolw = tolh;
case "buttonClass" : tolw = wdg.text == ""? tolh : tolw; break; break;
case "scrollBox" :
tolw = ui(96);
_tool_font = f_p2;
break;
case "buttonClass" :
tolw = wdg.text == ""? tolh : tolw;
break;
} }
var params = new widgetParam(tolx, toly, tolw, tolh, atr[$ key],, [ mx, my ]) var params = new widgetParam(tolx, toly, tolw, tolh, atr[$ key],, [ mx, my ])
params.s = tolh; params.s = tolh;
params.font = f_p3; params.font = _tool_font;
wdg.drawParam(params); wdg.drawParam(params);

View file

@ -113,6 +113,7 @@
PREFERENCES.node_param_show = false; PREFERENCES.node_param_show = false;
PREFERENCES.node_param_width = 192; PREFERENCES.node_param_width = 192;
PREFERENCES.node_3d_preview_size = 256;
#endregion #endregion

View file

@ -4,15 +4,27 @@ enum QUARTERNION_DISPLAY {
} }
function quarternionBox(_onModify) : widget() constructor { function quarternionBox(_onModify) : widget() constructor {
onModify = _onModify; onModify = _onModify;
current_value = []; current_value = [ 0, 0, 0, 0 ];
current_unit = QUARTERNION_DISPLAY.quarterion;
onModifyIndex = function(index, val) { onModifyIndex = function(index, val) {
var v = toNumber(val); var v = toNumber(val);
if(is_callable(onModify)) if(current_unit == QUARTERNION_DISPLAY.quarterion) {
return onModify(index, v); return onModify(index, v);
return noone;
} else {
var v = toNumber(val);
var qv = [
current_value[0],
current_value[1],
current_value[2],
];
qv[index] = v;
return onModify(noone, qv);
}
} }
size = 4; size = 4;
@ -79,8 +91,6 @@ function quarternionBox(_onModify) : widget() constructor {
if(array_empty(_data)) return 0; if(array_empty(_data)) return 0;
if(is_array(_data[0])) return 0; if(is_array(_data[0])) return 0;
current_value = _data;
var _bs = min(_h, ui(32)); var _bs = min(_h, ui(32));
var _disp = struct_try_get(_display_data, "angle_display"); var _disp = struct_try_get(_display_data, "angle_display");
@ -88,7 +98,7 @@ function quarternionBox(_onModify) : widget() constructor {
var bx = _x + _w - _bs; var bx = _x + _w - _bs;
var by = _y + _h / 2 - _bs / 2; var by = _y + _h / 2 - _bs / 2;
tooltip.index = _disp; tooltip.index = _disp;
if(buttonInstant(THEME.button_hide, bx, by, _bs, _bs, _m, iactive, ihover, tooltip, THEME.unit_angle, _disp, c_white) == 2) { if(buttonInstant(THEME.button_hide, bx, by, _bs, _bs, _m, iactive, ihover, tooltip, THEME.unit_angle, _disp, c_white) == 2) {
clickable = false; clickable = false;
_display_data.angle_display = (_disp + 1) % 2; _display_data.angle_display = (_disp + 1) % 2;
@ -96,6 +106,17 @@ function quarternionBox(_onModify) : widget() constructor {
_w -= _bs + ui(8); _w -= _bs + ui(8);
} }
current_unit = _display_data.angle_display;
if(current_unit == QUARTERNION_DISPLAY.quarterion || (!tb[0].sliding && !tb[1].sliding && !tb[2].sliding)) {
current_value[0] = _data[0];
current_value[1] = _data[1];
current_value[2] = _data[2];
if(current_unit == QUARTERNION_DISPLAY.quarterion)
current_value[3] = _data[3];
}
size = _disp? 3 : 4; size = _disp? 3 : 4;
var ww = _w / size; var ww = _w / size;
var bx = _x; var bx = _x;
@ -124,7 +145,6 @@ function quarternionBox(_onModify) : widget() constructor {
static clone = function() { #region static clone = function() { #region
var cln = new quarternionBox(onModify); var cln = new quarternionBox(onModify);
return cln; return cln;
} #endregion } #endregion
} }

View file

@ -24,7 +24,7 @@ function shell_execute(path, command, ref = noone) { #region
return res; return res;
} #endregion } #endregion
function shell_execute_async(path, command, ref = noone) { #region function shell_execute_async(path, command, ref = noone, _log = true) { #region
INLINE INLINE
if(IS_CMD) return shell_execute(path, command, ref); if(IS_CMD) return shell_execute(path, command, ref);
@ -36,7 +36,7 @@ function shell_execute_async(path, command, ref = noone) { #region
var txt = $"{path} {command}"; var txt = $"{path} {command}";
var res = ProcessExecuteAsync(txt); var res = ProcessExecuteAsync(txt);
print($"Execute async {path} {command} | {res}"); if(_log) print($"Execute async {path} {command} | {res}");
return res; return res;
} #endregion } #endregion

View file

@ -65,12 +65,5 @@ function filename_name_only(name) { #region
return string_replace(name, filename_ext(name), "") return string_replace(name, filename_ext(name), "")
} #endregion } #endregion
function string_to_var(str) { #region function string_to_var(str) { INLINE return string_replace_all(string_lower(str), " ", "_"); }
INLINE function string_quote(str) { INLINE return $"\"{str}\""; }
return string_replace_all(string_lower(str), " ", "_");
} #endregion
function string_quote(str) { #region
INLINE
return $"\"{str}\"";
} #endregion

View file

@ -6,9 +6,9 @@ enum TEXTBOX_INPUT {
function textBox(_input, _onModify) : textInput(_input, _onModify) constructor { function textBox(_input, _onModify) : textInput(_input, _onModify) constructor {
onRelease = noone; onRelease = noone;
align = _input == TEXTBOX_INPUT.number? fa_center : fa_left; align = _input == TEXTBOX_INPUT.number? fa_center : fa_left;
hide = false; hide = false;
color = COLORS._main_text; color = COLORS._main_text;
boxColor = c_white; boxColor = c_white;
format = TEXT_AREA_FORMAT._default; format = TEXT_AREA_FORMAT._default;
precision = 5; precision = 5;
@ -497,8 +497,10 @@ function textBox(_input, _onModify) : textInput(_input, _onModify) constructor {
draw_set_alpha(1); draw_set_alpha(1);
} }
} }
var _dpx = disp_x;
disp_x = lerp_float(disp_x, disp_x_to, 5); disp_x = lerp_float(disp_x, disp_x_to, 5);
if(_dpx != disp_x) _update = true;
var hoverRect = point_in_rectangle(_m[0], _m[1], _x, _y, _x + _w, _y + _h); var hoverRect = point_in_rectangle(_m[0], _m[1], _x, _y, _x + _w, _y + _h);