2025-12-01
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
from . import ffengine
|
||||
from .aabb import AABB, AABB_t
|
||||
from .fluidsimulation import FluidSimulation, MarkerParticle_t, DiffuseParticle_t
|
||||
from .meshobject import MeshObject
|
||||
from .meshfluidsource import MeshFluidSource
|
||||
from .forcefieldgrid import ForceFieldGrid
|
||||
from .forcefield import ForceField
|
||||
from .forcefieldpoint import ForceFieldPoint
|
||||
from .forcefieldsurface import ForceFieldSurface
|
||||
from .forcefieldvolume import ForceFieldVolume
|
||||
from .forcefieldcurve import ForceFieldCurve
|
||||
from .trianglemesh import TriangleMesh, TriangleMesh_t
|
||||
from .gridindex import GridIndex, GridIndex_t
|
||||
from .vector3 import Vector3, Vector3_t
|
||||
from . import mixbox
|
||||
@@ -0,0 +1,237 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
from .vector3 import Vector3, Vector3_t
|
||||
from .gridindex import GridIndex
|
||||
from . import method_decorators as decorators
|
||||
import ctypes
|
||||
|
||||
class AABB_t(ctypes.Structure):
|
||||
_fields_ = [("position", Vector3_t),
|
||||
("width", ctypes.c_float),
|
||||
("height", ctypes.c_float),
|
||||
("depth", ctypes.c_float)]
|
||||
|
||||
class AABB(object):
|
||||
|
||||
def __init__(self, *args):
|
||||
if len(args) == 4 and isinstance(args[0], Vector3):
|
||||
self.position = args[0]
|
||||
self.width = args[1]
|
||||
self.height = args[2]
|
||||
self.depth = args[3]
|
||||
elif len(args) == 6:
|
||||
self._position = Vector3(args[0], args[1], args[2])
|
||||
self.width = args[3]
|
||||
self.height = args[4]
|
||||
self.depth = args[5]
|
||||
elif len(args) == 0:
|
||||
self.position = Vector3()
|
||||
self.width = 0.0
|
||||
self.height = 0.0
|
||||
self.depth = 0.0
|
||||
else:
|
||||
errmsg = "AABB must be initialized with types:\n"
|
||||
errmsg += "x: " + (str(float) + "\n" +
|
||||
"y: " + str(float) + "\n" +
|
||||
"z: " + str(float) + "\n" +
|
||||
"width: " + str(float) + "\n" +
|
||||
"height: " + str(float) + "\n" +
|
||||
"depth: " + str(float) + "\n\n" +
|
||||
"or\n\n" +
|
||||
"position: " + (str(Vector3)) + "\n" +
|
||||
"width: " + str(float) + "\n" +
|
||||
"height: " + str(float) + "\n" +
|
||||
"depth: " + str(float))
|
||||
raise TypeError(errmsg)
|
||||
|
||||
def __str__(self):
|
||||
return (str(self.position) + " " + str(self.width) + " " +
|
||||
str(self.height) + " " +
|
||||
str(self.depth))
|
||||
|
||||
@classmethod
|
||||
@decorators.check_type(Vector3)
|
||||
def from_corners(cls, pmin = Vector3(), pmax = Vector3()):
|
||||
minx = min(pmin.x, pmax.x)
|
||||
miny = min(pmin.y, pmax.y)
|
||||
minz = min(pmin.z, pmax.z)
|
||||
maxx = max(pmin.x, pmax.x)
|
||||
maxy = max(pmin.y, pmax.y)
|
||||
maxz = max(pmin.z, pmax.z)
|
||||
width = maxx - minx
|
||||
height = maxy - miny
|
||||
depth = maxz - minz
|
||||
|
||||
return cls(minx, miny, minz, width, height, depth)
|
||||
|
||||
@classmethod
|
||||
def from_points(cls, point_list):
|
||||
if len(point_list) == 0:
|
||||
return cls()
|
||||
|
||||
minx, miny, minz = point_list[0]
|
||||
maxx, maxy, maxz = point_list[0]
|
||||
for p in point_list:
|
||||
minx = min(p.x, minx);
|
||||
miny = min(p.y, miny);
|
||||
minz = min(p.z, minz);
|
||||
maxx = max(p.x, maxx);
|
||||
maxy = max(p.y, maxy);
|
||||
maxz = max(p.z, maxz);
|
||||
|
||||
eps = 1e-9;
|
||||
width = maxx - minx + eps;
|
||||
height = maxy - miny + eps;
|
||||
depth = maxz - minz + eps;
|
||||
|
||||
return cls(minx, miny, minz, width, height, depth)
|
||||
|
||||
@classmethod
|
||||
def from_struct(cls, cstruct):
|
||||
return cls(Vector3.from_struct(cstruct.position),
|
||||
float(cstruct.width),
|
||||
float(cstruct.height),
|
||||
float(cstruct.depth))
|
||||
|
||||
def to_struct(self):
|
||||
return AABB_t(Vector3_t(self.x, self.y, self.z),
|
||||
self.width, self.height, self.depth)
|
||||
|
||||
@classmethod
|
||||
def from_grid_index(cls, grid_index = GridIndex(), dx = 0.0):
|
||||
return cls(grid_index.i*dx, grid_index.j*dx, grid_index.k*dx, dx, dx, dx)
|
||||
|
||||
@property
|
||||
def x(self):
|
||||
return self._position.x
|
||||
|
||||
@property
|
||||
def y(self):
|
||||
return self._position.y
|
||||
|
||||
@property
|
||||
def z(self):
|
||||
return self._position.z
|
||||
|
||||
@property
|
||||
def width(self):
|
||||
return self._width
|
||||
|
||||
@property
|
||||
def height(self):
|
||||
return self._height
|
||||
|
||||
@property
|
||||
def depth(self):
|
||||
return self._depth
|
||||
|
||||
@property
|
||||
def position(self):
|
||||
return self._position
|
||||
|
||||
@x.setter
|
||||
def x(self, value):
|
||||
self._position.x = value
|
||||
|
||||
@y.setter
|
||||
def y(self, value):
|
||||
self._position.y = value
|
||||
|
||||
@z.setter
|
||||
def z(self, value):
|
||||
self._position.z = value
|
||||
|
||||
@width.setter
|
||||
def width(self, value):
|
||||
self._width = float(value)
|
||||
|
||||
@height.setter
|
||||
def height(self, value):
|
||||
self._height = float(value)
|
||||
|
||||
@depth.setter
|
||||
def depth(self, value):
|
||||
self._depth = float(value)
|
||||
|
||||
@position.setter
|
||||
@decorators.check_type(Vector3)
|
||||
def position(self, vector):
|
||||
self._position = vector
|
||||
|
||||
def expand(self, v):
|
||||
h = 0.5 * v;
|
||||
self.position -= Vector3(h, h, h);
|
||||
self.width += v;
|
||||
self.height += v;
|
||||
self.depth += v;
|
||||
|
||||
@decorators.xyz_or_vector
|
||||
def contains_point(self, x, y, z):
|
||||
return (x >= self.x and y >= self.y and z >= self.z and
|
||||
x < self.x + self.width and
|
||||
y < self.y + self.height and
|
||||
z < self.z + self.depth)
|
||||
|
||||
def get_min_point(self):
|
||||
return self.position
|
||||
|
||||
def get_max_point(self):
|
||||
return self.position + Vector3(self.width, self.height, self.depth)
|
||||
|
||||
def get_intersection(self, bbox):
|
||||
minp1 = self.get_min_point()
|
||||
minp2 = bbox.get_min_point()
|
||||
maxp1 = self.get_max_point()
|
||||
maxp2 = bbox.get_max_point()
|
||||
|
||||
if minp1.x > maxp2.x or minp1.y > maxp2.y or minp1.z > maxp2.z:
|
||||
return AABB()
|
||||
|
||||
interminx = max(minp1.x, minp2.x)
|
||||
interminy = max(minp1.y, minp2.y)
|
||||
interminz = max(minp1.z, minp2.z)
|
||||
intermaxx = min(maxp1.x, maxp2.x)
|
||||
intermaxy = min(maxp1.y, maxp2.y)
|
||||
intermaxz = min(maxp1.z, maxp2.z)
|
||||
|
||||
return AABB.from_corners(Vector3(interminx, interminy, interminz),
|
||||
Vector3(intermaxx, intermaxy, intermaxz))
|
||||
|
||||
def get_union(self, bbox):
|
||||
minp1 = self.get_min_point()
|
||||
minp2 = bbox.get_min_point()
|
||||
maxp1 = self.get_max_point()
|
||||
maxp2 = bbox.get_max_point()
|
||||
|
||||
if minp1.x > maxp2.x or minp1.y > maxp2.y or minp1.z > maxp2.z:
|
||||
return AABB()
|
||||
|
||||
unionminx = min(minp1.x, minp2.x)
|
||||
unionminy = min(minp1.y, minp2.y)
|
||||
unionminz = min(minp1.z, minp2.z)
|
||||
unionmaxx = max(maxp1.x, maxp2.x)
|
||||
unionmaxy = max(maxp1.y, maxp2.y)
|
||||
unionmaxz = max(maxp1.z, maxp2.z)
|
||||
|
||||
return AABB.from_corners(Vector3(unionminx, unionminy, unionminz),
|
||||
Vector3(unionmaxx, unionmaxy, unionmaxz))
|
||||
@@ -0,0 +1,112 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
from abc import ABCMeta, abstractmethod
|
||||
import array
|
||||
from gridindex import GridIndex
|
||||
import method_decorators as decorators
|
||||
|
||||
class Array3d:
|
||||
__metaclass__ = ABCMeta
|
||||
|
||||
def __init__(self, isize, jsize, ksize):
|
||||
self.width, self.height, self.depth = isize, jsize, ksize
|
||||
self._num_elements = isize*jsize*ksize
|
||||
|
||||
@abstractmethod
|
||||
def _init_grid(self, data):
|
||||
pass
|
||||
|
||||
def fill(self, value):
|
||||
for i in range(self._num_elements):
|
||||
self._grid[i] = value
|
||||
|
||||
@decorators.ijk_or_gridindex
|
||||
def __call__(self, i, j, k):
|
||||
if not self._is_index_in_range(i, j, k) and self._out_of_range_value != None:
|
||||
return self._out_of_range_value
|
||||
return self._grid[self._get_flat_index(i, j, k)]
|
||||
|
||||
def __iter__(self):
|
||||
i = j = k = 0
|
||||
for v in self._grid:
|
||||
yield i, j, k, v
|
||||
i += 1
|
||||
if i >= self.width:
|
||||
i = 0
|
||||
j += 1
|
||||
if j >= self.height:
|
||||
j = 0
|
||||
k += 1
|
||||
|
||||
@decorators.ijk_or_gridindex
|
||||
def get(self, i, j, k):
|
||||
return self(i, j, k)
|
||||
|
||||
@decorators.ijk_or_gridindex_and_value
|
||||
def set(self, i, j, k, value):
|
||||
self._grid[self._get_flat_index(i, j, k)] = value
|
||||
|
||||
@decorators.ijk_or_gridindex_and_value
|
||||
def add(self, i, j, k, value):
|
||||
self._grid[self._get_flat_index(i, j, k)] += value
|
||||
|
||||
def get_num_elements(self):
|
||||
return self._num_elements
|
||||
|
||||
def set_out_of_range_value(self, value = None):
|
||||
self._out_of_range_value = value
|
||||
|
||||
def get_out_of_range_value(self):
|
||||
return self._out_of_range_value
|
||||
|
||||
def _is_index_in_range(self, i, j, k):
|
||||
return (i >= 0 and j >= 0 and k >= 0 and
|
||||
i < self.width and j < self.height and k < self.depth)
|
||||
|
||||
def _get_flat_index(self, i, j, k):
|
||||
return i + self.width*(j + self.height*k)
|
||||
|
||||
|
||||
class Array3di(Array3d):
|
||||
def __init__(self, isize, jsize, ksize, default_value = int()):
|
||||
Array3d.__init__(self, isize, jsize, ksize)
|
||||
self._init_grid(default_value)
|
||||
|
||||
def _init_grid(self, default_value):
|
||||
self._grid = array.array('i', [default_value]*self.get_num_elements())
|
||||
|
||||
class Array3df(Array3d):
|
||||
def __init__(self, isize, jsize, ksize, default_value = float()):
|
||||
Array3d.__init__(self, isize, jsize, ksize)
|
||||
self._init_grid(default_value)
|
||||
|
||||
def _init_grid(self, default_value):
|
||||
self._grid = array.array('f', [default_value]*self.get_num_elements())
|
||||
|
||||
class Array3dd(Array3d):
|
||||
def __init__(self, isize, jsize, ksize, default_value = float()):
|
||||
Array3d.__init__(self, isize, jsize, ksize)
|
||||
self._init_grid(default_value)
|
||||
|
||||
def _init_grid(self, default_value):
|
||||
self._grid = array.array('d', [default_value]*self.get_num_elements())
|
||||
@@ -0,0 +1,138 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import ctypes
|
||||
import os
|
||||
import platform
|
||||
|
||||
|
||||
class LibraryLoadError(Exception):
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
def __str__(self):
|
||||
return repr(self.value)
|
||||
|
||||
|
||||
# The FFEngineLib class loads the FLIP Fluids addon simulation engine. The engine
|
||||
# is a dynamic library which contains methods to process simulation calculations.
|
||||
# The simulation engine is written in C and C++ and is controlled through Python
|
||||
# using the built-in ctypes module (https://docs.python.org/3/library/ctypes.html).
|
||||
#
|
||||
# The files in src/engine/ffengine contain Python bindings for the fluid simulation
|
||||
# objects and methods. The Python bindings use ctypes to call corresponding C bindings
|
||||
# found in src/engine/c_bindings. The C bindings call C++ methods found in src/engine.
|
||||
#
|
||||
# To begin following how the simulator is run from Python to C to C++, refer to the
|
||||
# baking script located at src/addon/bake.py starting at the bake(...) method. The
|
||||
# arguments passed to bake(...) are generated and formed in the addon within the Bake
|
||||
# Operators found in src/addon/operators/bake_operators.py as well as the Export
|
||||
# Operators found in src/addon/operators/export_operators.py.
|
||||
class FFEngineLib():
|
||||
def __init__(self):
|
||||
self._lib = None
|
||||
|
||||
def __getattr__(self, name):
|
||||
if self.__dict__['_lib'] is None:
|
||||
self._lib = self._load_library("ffengine")
|
||||
|
||||
return getattr(self._lib, name)
|
||||
|
||||
def _load_library(self, name):
|
||||
libname_release_prefix = "libffengine"
|
||||
|
||||
system = platform.system()
|
||||
if system == "Windows":
|
||||
library_extension = ".dll"
|
||||
elif system == "Darwin":
|
||||
library_extension = ".dylib"
|
||||
elif system == "Linux":
|
||||
library_extension = ".so"
|
||||
else:
|
||||
raise LibraryLoadError("Unable to recognize system: " + system)
|
||||
|
||||
libdir = os.path.join(os.path.dirname(__file__), "lib")
|
||||
libnames= [f for f in os.listdir(libdir) if os.path.isfile(os.path.join(libdir, f))]
|
||||
libnames_release = [n for n in libnames if n.startswith(libname_release_prefix) and n.endswith(library_extension)]
|
||||
|
||||
# Sorting the library names by length is not necessary, but sorting from
|
||||
# longest name to shortest will bypass a possible user-error if the user does not
|
||||
# completely remove the previous installation before installing a new version.
|
||||
# A version update required a possible increase in the length of the library
|
||||
# name. This sort will ensure that the longer library name (newer version)
|
||||
# is used before the shorter named file (older version) that could remain
|
||||
# from an incorrect install or compile process.
|
||||
libnames_release.sort(key=len, reverse=True)
|
||||
|
||||
libpaths_release = [os.path.join(libdir, n) for n in libnames_release]
|
||||
|
||||
# The addon requires a functioning ffengine library version
|
||||
missing_libraries = []
|
||||
if not libpaths_release:
|
||||
missing_libraries.append(libname_release_prefix + library_extension)
|
||||
|
||||
if missing_libraries:
|
||||
err_msg = "Cannot find fluid engine libraries: "
|
||||
for libname in missing_libraries:
|
||||
err_msg += "<" + libname + "> "
|
||||
raise LibraryLoadError(err_msg)
|
||||
|
||||
# The addon may be packaged with multiple versions of a library for the OS, not
|
||||
# all of which may be compatible with the specific OS version. Choose the first
|
||||
# library that loads without error.
|
||||
# Refer to the LIBRARY_SUFFIX variable in the CMakeLists.txt file for generating a
|
||||
# library with a suffix added to the name.
|
||||
loaded_library = None
|
||||
failed_libraries = []
|
||||
for libpath in libpaths_release:
|
||||
try:
|
||||
loaded_library = ctypes.cdll.LoadLibrary(libpath)
|
||||
break
|
||||
except:
|
||||
failed_libraries.append(libpath)
|
||||
loaded_library = None
|
||||
pass
|
||||
|
||||
# Additional notes on the error message:
|
||||
# (1) Blender 2.80 and later are 64-bit and require a library that has been
|
||||
# built as 64-bit. Make sure you are using a 64-bit compiler for these versions.
|
||||
# Blender 2.79 distributes both 32-bit and 64-bit versions, so make sure your
|
||||
# your compiler matches the target version of Blender 2.79.
|
||||
# (2) This resolves possible errors due to incorrect installation of the addon and
|
||||
# possible conflicts between Blender versions (such as multiple daily builds).
|
||||
# Refer to this document for addon installation troubleshooting:
|
||||
# https://github.com/rlguy/Blender-FLIP-Fluids/wiki/Addon-Installation-Troubleshooting
|
||||
if loaded_library is None:
|
||||
failed_libraries_string = ""
|
||||
for libpath in failed_libraries:
|
||||
failed_libraries_string += "<" + libpath + "> "
|
||||
|
||||
msg = "Unable to load fluid engine libraries: " + failed_libraries_string
|
||||
msg += " (1) Make sure that you are using a 64-bit version of Python/Blender"
|
||||
msg += " if built for 64-bit and likewise if built for 32-bit."
|
||||
msg += " (2) Try clearing your Blender user settings (make a backup first!)."
|
||||
msg += " (3) Contact the developers if you think that this is an error."
|
||||
raise LibraryLoadError(msg)
|
||||
|
||||
return loaded_library
|
||||
|
||||
|
||||
ffengine = FFEngineLib()
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,231 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from ctypes import c_void_p, c_char_p, c_int, c_float, c_double, byref
|
||||
|
||||
from .ffengine import ffengine as lib
|
||||
from . import pybindings as pb
|
||||
from . import method_decorators as decorators
|
||||
from .trianglemesh import TriangleMesh_t
|
||||
|
||||
class ForceField():
|
||||
__metaclass__ = ABCMeta
|
||||
|
||||
@abstractmethod
|
||||
def __init__():
|
||||
pass
|
||||
|
||||
def __call__(self):
|
||||
return self._obj
|
||||
|
||||
def update_mesh_static(self, mesh):
|
||||
mesh_struct = mesh.to_struct()
|
||||
libfunc = lib.ForceField_update_mesh_static
|
||||
args = [c_void_p, TriangleMesh_t, c_void_p]
|
||||
pb.init_lib_func(libfunc, args, c_void_p)
|
||||
pb.execute_lib_func(libfunc, [self(), mesh_struct])
|
||||
|
||||
def update_mesh_animated(self, mesh_previous, mesh_current, mesh_next):
|
||||
mesh_struct_previous = mesh_previous.to_struct()
|
||||
mesh_struct_current = mesh_current.to_struct()
|
||||
mesh_struct_next = mesh_next.to_struct()
|
||||
libfunc = lib.ForceField_update_mesh_animated
|
||||
args = [c_void_p, TriangleMesh_t, TriangleMesh_t, TriangleMesh_t, c_void_p]
|
||||
pb.init_lib_func(libfunc, args, c_void_p)
|
||||
pb.execute_lib_func(libfunc, [self(), mesh_struct_previous,
|
||||
mesh_struct_current,
|
||||
mesh_struct_next])
|
||||
|
||||
@property
|
||||
def enable(self):
|
||||
libfunc = lib.ForceField_is_enabled
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_int)
|
||||
return bool(pb.execute_lib_func(libfunc, [self()]))
|
||||
|
||||
@enable.setter
|
||||
def enable(self, boolval):
|
||||
if boolval:
|
||||
libfunc = lib.ForceField_enable
|
||||
else:
|
||||
libfunc = lib.ForceField_disable
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@property
|
||||
def strength(self):
|
||||
libfunc = lib.ForceField_get_strength
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_float)
|
||||
return pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@strength.setter
|
||||
def strength(self, value):
|
||||
libfunc = lib.ForceField_set_strength
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_float, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self(), value])
|
||||
|
||||
@property
|
||||
def falloff_power(self):
|
||||
libfunc = lib.ForceField_get_falloff_power
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_float)
|
||||
return pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@falloff_power.setter
|
||||
def falloff_power(self, value):
|
||||
libfunc = lib.ForceField_set_falloff_power
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_float, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self(), value])
|
||||
|
||||
@property
|
||||
def max_force_limit_factor(self):
|
||||
libfunc = lib.ForceField_get_max_force_limit_factor
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_float)
|
||||
return pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@max_force_limit_factor.setter
|
||||
def max_force_limit_factor(self, value):
|
||||
libfunc = lib.ForceField_set_max_force_limit_factor
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_float, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self(), value])
|
||||
|
||||
@property
|
||||
def enable_min_distance(self):
|
||||
libfunc = lib.ForceField_is_min_distance_enabled
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_int)
|
||||
return bool(pb.execute_lib_func(libfunc, [self()]))
|
||||
|
||||
@enable_min_distance.setter
|
||||
def enable_min_distance(self, boolval):
|
||||
if boolval:
|
||||
libfunc = lib.ForceField_enable_min_distance
|
||||
else:
|
||||
libfunc = lib.ForceField_disable_min_distance
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@property
|
||||
def min_distance(self):
|
||||
libfunc = lib.ForceField_get_min_distance
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_float)
|
||||
return pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@min_distance.setter
|
||||
def min_distance(self, value):
|
||||
libfunc = lib.ForceField_set_min_distance
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_float, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self(), value])
|
||||
|
||||
@property
|
||||
def enable_max_distance(self):
|
||||
libfunc = lib.ForceField_is_max_distance_enabled
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_int)
|
||||
return bool(pb.execute_lib_func(libfunc, [self()]))
|
||||
|
||||
@enable_max_distance.setter
|
||||
def enable_max_distance(self, boolval):
|
||||
if boolval:
|
||||
libfunc = lib.ForceField_enable_max_distance
|
||||
else:
|
||||
libfunc = lib.ForceField_disable_max_distance
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@property
|
||||
def max_distance(self):
|
||||
libfunc = lib.ForceField_get_max_distance
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_float)
|
||||
return pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@max_distance.setter
|
||||
def max_distance(self, value):
|
||||
libfunc = lib.ForceField_set_max_distance
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_float, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self(), value])
|
||||
|
||||
@property
|
||||
def enable_frontfacing(self):
|
||||
libfunc = lib.ForceField_is_frontfacing_enabled
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_int)
|
||||
return bool(pb.execute_lib_func(libfunc, [self()]))
|
||||
|
||||
@enable_frontfacing.setter
|
||||
def enable_frontfacing(self, boolval):
|
||||
if boolval:
|
||||
libfunc = lib.ForceField_enable_frontfacing
|
||||
else:
|
||||
libfunc = lib.ForceField_disable_frontfacing
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@property
|
||||
def enable_backfacing(self):
|
||||
libfunc = lib.ForceField_is_backfacing_enabled
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_int)
|
||||
return bool(pb.execute_lib_func(libfunc, [self()]))
|
||||
|
||||
@enable_backfacing.setter
|
||||
def enable_backfacing(self, boolval):
|
||||
if boolval:
|
||||
libfunc = lib.ForceField_enable_backfacing
|
||||
else:
|
||||
libfunc = lib.ForceField_disable_backfacing
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@property
|
||||
def enable_edgefacing(self):
|
||||
libfunc = lib.ForceField_is_edgefacing_enabled
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_int)
|
||||
return bool(pb.execute_lib_func(libfunc, [self()]))
|
||||
|
||||
@enable_edgefacing.setter
|
||||
def enable_edgefacing(self, boolval):
|
||||
if boolval:
|
||||
libfunc = lib.ForceField_enable_edgefacing
|
||||
else:
|
||||
libfunc = lib.ForceField_disable_edgefacing
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@property
|
||||
def gravity_scale(self):
|
||||
libfunc = lib.ForceField_get_gravity_scale
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_float)
|
||||
return pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@gravity_scale.setter
|
||||
def gravity_scale(self, value):
|
||||
libfunc = lib.ForceField_set_gravity_scale
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_float, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self(), value])
|
||||
|
||||
@property
|
||||
def gravity_scale_width(self):
|
||||
libfunc = lib.ForceField_get_gravity_scale
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_float)
|
||||
return pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@gravity_scale_width.setter
|
||||
def gravity_scale_width(self, value):
|
||||
libfunc = lib.ForceField_set_gravity_scale_width
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_float, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self(), value])
|
||||
@@ -0,0 +1,87 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from ctypes import c_void_p, c_char_p, c_int, c_float, c_double, byref
|
||||
|
||||
from .ffengine import ffengine as lib
|
||||
from .forcefield import ForceField
|
||||
from . import pybindings as pb
|
||||
from . import method_decorators as decorators
|
||||
|
||||
class ForceFieldCurve(ForceField):
|
||||
|
||||
def __init__(self):
|
||||
libfunc = lib.ForceFieldCurve_new
|
||||
args = [c_void_p]
|
||||
pb.init_lib_func(libfunc, args, c_void_p)
|
||||
self._obj = pb.execute_lib_func(libfunc, [])
|
||||
|
||||
def __del__(self):
|
||||
try:
|
||||
libfunc = lib.ForceFieldCurve_destroy
|
||||
pb.init_lib_func(libfunc, [c_void_p], None)
|
||||
libfunc(self._obj)
|
||||
except:
|
||||
pass
|
||||
|
||||
def __call__(self):
|
||||
return self._obj
|
||||
|
||||
@property
|
||||
def flow_strength(self):
|
||||
libfunc = lib.ForceFieldCurve_get_flow_strength
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_float)
|
||||
return pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@flow_strength.setter
|
||||
def flow_strength(self, value):
|
||||
libfunc = lib.ForceFieldCurve_set_flow_strength
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_float, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self(), value])
|
||||
|
||||
@property
|
||||
def spin_strength(self):
|
||||
libfunc = lib.ForceFieldCurve_get_spin_strength
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_float)
|
||||
return pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@spin_strength.setter
|
||||
def spin_strength(self, value):
|
||||
libfunc = lib.ForceFieldCurve_set_spin_strength
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_float, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self(), value])
|
||||
|
||||
@property
|
||||
def enable_endcaps(self):
|
||||
libfunc = lib.ForceFieldCurve_is_endcaps_enabled
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_int)
|
||||
return bool(pb.execute_lib_func(libfunc, [self()]))
|
||||
|
||||
@enable_endcaps.setter
|
||||
def enable_endcaps(self, boolval):
|
||||
if boolval:
|
||||
libfunc = lib.ForceFieldCurve_enable_endcaps
|
||||
else:
|
||||
libfunc = lib.ForceFieldCurve_disable_endcaps
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self()])
|
||||
@@ -0,0 +1,59 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
from ctypes import c_void_p, c_char_p, c_int, c_float, c_double, byref
|
||||
|
||||
from .ffengine import ffengine as lib
|
||||
from . import pybindings as pb
|
||||
from . import method_decorators as decorators
|
||||
|
||||
class ForceFieldGrid():
|
||||
|
||||
def __init__(self, c_pointer=None):
|
||||
if c_pointer is not None:
|
||||
self._obj = c_pointer
|
||||
self._is_owner = False
|
||||
else:
|
||||
libfunc = lib.ForceFieldGrid_new
|
||||
args = [c_void_p]
|
||||
pb.init_lib_func(libfunc, args, c_void_p)
|
||||
self._obj = pb.execute_lib_func(libfunc, [])
|
||||
self._is_owner = True
|
||||
|
||||
def __del__(self):
|
||||
if not self._is_owner:
|
||||
return
|
||||
try:
|
||||
libfunc = lib.MeshObject_destroy
|
||||
pb.init_lib_func(libfunc, [c_void_p], None)
|
||||
libfunc(self._obj)
|
||||
except:
|
||||
pass
|
||||
|
||||
def __call__(self):
|
||||
return self._obj
|
||||
|
||||
def add_force_field(self, field):
|
||||
libfunc = lib.ForceFieldGrid_add_force_field
|
||||
args = [c_void_p, c_void_p, c_void_p]
|
||||
pb.init_lib_func(libfunc, args, None)
|
||||
pb.execute_lib_func(libfunc, [self(), field()])
|
||||
@@ -0,0 +1,45 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from ctypes import c_void_p, c_char_p, c_int, c_float, c_double, byref
|
||||
|
||||
from .ffengine import ffengine as lib
|
||||
from .forcefield import ForceField
|
||||
from . import pybindings as pb
|
||||
from . import method_decorators as decorators
|
||||
|
||||
class ForceFieldPoint(ForceField):
|
||||
|
||||
def __init__(self):
|
||||
libfunc = lib.ForceFieldPoint_new
|
||||
args = [c_void_p]
|
||||
pb.init_lib_func(libfunc, args, c_void_p)
|
||||
self._obj = pb.execute_lib_func(libfunc, [])
|
||||
|
||||
def __del__(self):
|
||||
try:
|
||||
libfunc = lib.ForceFieldPoint_destroy
|
||||
pb.init_lib_func(libfunc, [c_void_p], None)
|
||||
libfunc(self._obj)
|
||||
except:
|
||||
pass
|
||||
@@ -0,0 +1,45 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from ctypes import c_void_p, c_char_p, c_int, c_float, c_double, byref
|
||||
|
||||
from .ffengine import ffengine as lib
|
||||
from .forcefield import ForceField
|
||||
from . import pybindings as pb
|
||||
from . import method_decorators as decorators
|
||||
|
||||
class ForceFieldSurface(ForceField):
|
||||
|
||||
def __init__(self):
|
||||
libfunc = lib.ForceFieldSurface_new
|
||||
args = [c_void_p]
|
||||
pb.init_lib_func(libfunc, args, c_void_p)
|
||||
self._obj = pb.execute_lib_func(libfunc, [])
|
||||
|
||||
def __del__(self):
|
||||
try:
|
||||
libfunc = lib.ForceFieldSurface_destroy
|
||||
pb.init_lib_func(libfunc, [c_void_p], None)
|
||||
libfunc(self._obj)
|
||||
except:
|
||||
pass
|
||||
@@ -0,0 +1,45 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from ctypes import c_void_p, c_char_p, c_int, c_float, c_double, byref
|
||||
|
||||
from .ffengine import ffengine as lib
|
||||
from .forcefield import ForceField
|
||||
from . import pybindings as pb
|
||||
from . import method_decorators as decorators
|
||||
|
||||
class ForceFieldVolume(ForceField):
|
||||
|
||||
def __init__(self):
|
||||
libfunc = lib.ForceFieldVolume_new
|
||||
args = [c_void_p]
|
||||
pb.init_lib_func(libfunc, args, c_void_p)
|
||||
self._obj = pb.execute_lib_func(libfunc, [])
|
||||
|
||||
def __del__(self):
|
||||
try:
|
||||
libfunc = lib.ForceFieldVolume_destroy
|
||||
pb.init_lib_func(libfunc, [c_void_p], None)
|
||||
libfunc(self._obj)
|
||||
except:
|
||||
pass
|
||||
@@ -0,0 +1,85 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import array
|
||||
import ctypes
|
||||
|
||||
class GridIndex_t(ctypes.Structure):
|
||||
_fields_ = [("i", ctypes.c_int),
|
||||
("j", ctypes.c_int),
|
||||
("k", ctypes.c_int)]
|
||||
|
||||
class GridIndex(object):
|
||||
|
||||
def __init__(self, i = 0, j = 0, k = 0):
|
||||
if isinstance(i, GridIndex):
|
||||
self._values = array.array('i', [i.i, i.j, i.k])
|
||||
else:
|
||||
self._values = array.array('i', [i, j, k])
|
||||
|
||||
def __str__(self):
|
||||
return str(self.i) + " " + str(self.j) + " " + str(self.k)
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key < 0 or key > 2:
|
||||
raise IndexError("Index must be in range [0, 2]")
|
||||
if not isinstance(key, int):
|
||||
raise TypeError("Index must be an integer")
|
||||
|
||||
return self._values[key]
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
if key < 0 or key > 2:
|
||||
raise IndexError("Index must be in range [0, 2]")
|
||||
if not isinstance(key, int):
|
||||
raise TypeError("Index must be an integer")
|
||||
|
||||
self._values[key] = value
|
||||
|
||||
def __iter__(self):
|
||||
yield self._values[0]
|
||||
yield self._values[1]
|
||||
yield self._values[2]
|
||||
|
||||
@property
|
||||
def i(self):
|
||||
return self._values[0]
|
||||
|
||||
@property
|
||||
def j(self):
|
||||
return self._values[1]
|
||||
|
||||
@property
|
||||
def k(self):
|
||||
return self._values[2]
|
||||
|
||||
@i.setter
|
||||
def i(self, value):
|
||||
self._values[0] = value
|
||||
|
||||
@j.setter
|
||||
def j(self, value):
|
||||
self._values[1] = value
|
||||
|
||||
@k.setter
|
||||
def k(self, value):
|
||||
self._values[2] = value
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,298 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
from ctypes import c_void_p, c_char_p, c_int, c_float, c_double, byref
|
||||
|
||||
from .ffengine import ffengine as lib
|
||||
from . import pybindings as pb
|
||||
from . import method_decorators as decorators
|
||||
from .trianglemesh import TriangleMesh_t
|
||||
|
||||
class MeshFluidSource():
|
||||
|
||||
def __init__(self, i, j, k, dx):
|
||||
libfunc = lib.MeshFluidSource_new
|
||||
args = [c_int, c_int, c_int, c_double, c_void_p]
|
||||
pb.init_lib_func(libfunc, args, c_void_p)
|
||||
self._obj = pb.execute_lib_func(libfunc, [i, j, k, dx])
|
||||
|
||||
def __del__(self):
|
||||
try:
|
||||
libfunc = lib.MeshFluidSource_destroy
|
||||
pb.init_lib_func(libfunc, [c_void_p], None)
|
||||
libfunc(self._obj)
|
||||
except:
|
||||
pass
|
||||
|
||||
def __call__(self):
|
||||
return self._obj
|
||||
|
||||
def update_mesh_static(self, mesh):
|
||||
mesh_struct = mesh.to_struct()
|
||||
libfunc = lib.MeshFluidSource_update_mesh_static
|
||||
args = [c_void_p, TriangleMesh_t, c_void_p]
|
||||
pb.init_lib_func(libfunc, args, c_void_p)
|
||||
pb.execute_lib_func(libfunc, [self(), mesh_struct])
|
||||
|
||||
def update_mesh_animated(self, mesh_previous, mesh_current, mesh_next):
|
||||
mesh_struct_previous = mesh_previous.to_struct()
|
||||
mesh_struct_current = mesh_current.to_struct()
|
||||
mesh_struct_next = mesh_next.to_struct()
|
||||
libfunc = lib.MeshFluidSource_update_mesh_animated
|
||||
args = [c_void_p, TriangleMesh_t, TriangleMesh_t, TriangleMesh_t, c_void_p]
|
||||
pb.init_lib_func(libfunc, args, c_void_p)
|
||||
pb.execute_lib_func(libfunc, [self(), mesh_struct_previous,
|
||||
mesh_struct_current,
|
||||
mesh_struct_next])
|
||||
|
||||
@property
|
||||
def enable(self):
|
||||
libfunc = lib.MeshFluidSource_is_enabled
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_int)
|
||||
return bool(pb.execute_lib_func(libfunc, [self()]))
|
||||
|
||||
@enable.setter
|
||||
def enable(self, boolval):
|
||||
if boolval:
|
||||
libfunc = lib.MeshFluidSource_enable
|
||||
else:
|
||||
libfunc = lib.MeshFluidSource_disable
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@property
|
||||
def substep_emissions(self):
|
||||
libfunc = lib.MeshFluidSource_get_substep_emissions
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_int)
|
||||
return pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@substep_emissions.setter
|
||||
def substep_emissions(self, n):
|
||||
libfunc = lib.MeshFluidSource_set_substep_emissions
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_int, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self(), int(n)])
|
||||
|
||||
@property
|
||||
def inflow(self):
|
||||
libfunc = lib.MeshFluidSource_is_inflow
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_int)
|
||||
return bool(pb.execute_lib_func(libfunc, [self()]))
|
||||
|
||||
@inflow.setter
|
||||
def inflow(self, boolval):
|
||||
if boolval:
|
||||
libfunc = lib.MeshFluidSource_set_inflow
|
||||
else:
|
||||
libfunc = lib.MeshFluidSource_set_outflow
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@property
|
||||
def outflow(self):
|
||||
libfunc = lib.MeshFluidSource_is_inflow
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_int)
|
||||
return bool(pb.execute_lib_func(libfunc, [self()]))
|
||||
|
||||
@outflow.setter
|
||||
def outflow(self, boolval):
|
||||
if boolval:
|
||||
libfunc = lib.MeshFluidSource_set_outflow
|
||||
else:
|
||||
libfunc = lib.MeshFluidSource_set_inflow
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@property
|
||||
def fluid_outflow(self):
|
||||
libfunc = lib.MeshFluidSource_is_fluid_outflow_enabled
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_int)
|
||||
return bool(pb.execute_lib_func(libfunc, [self()]))
|
||||
|
||||
@fluid_outflow.setter
|
||||
def fluid_outflow(self, boolval):
|
||||
if boolval:
|
||||
libfunc = lib.MeshFluidSource_enable_fluid_outflow
|
||||
else:
|
||||
libfunc = lib.MeshFluidSource_disable_fluid_outflow
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@property
|
||||
def diffuse_outflow(self):
|
||||
libfunc = lib.MeshFluidSource_is_diffuse_outflow_enabled
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_int)
|
||||
return bool(pb.execute_lib_func(libfunc, [self()]))
|
||||
|
||||
@diffuse_outflow.setter
|
||||
def diffuse_outflow(self, boolval):
|
||||
if boolval:
|
||||
libfunc = lib.MeshFluidSource_enable_diffuse_outflow
|
||||
else:
|
||||
libfunc = lib.MeshFluidSource_disable_diffuse_outflow
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
def get_velocity(self):
|
||||
libfunc = lib.MeshFluidSource_get_velocity
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], Vector3_t)
|
||||
cvect = pb.execute_lib_func(libfunc, [self()])
|
||||
return Vector3.from_struct(cvect)
|
||||
|
||||
@decorators.xyz_or_vector
|
||||
def set_velocity(self, vx, vy, vz):
|
||||
libfunc = lib.MeshFluidSource_set_velocity
|
||||
pb.init_lib_func(
|
||||
libfunc,
|
||||
[c_void_p, c_double, c_double, c_double, c_void_p], None
|
||||
)
|
||||
pb.execute_lib_func(libfunc, [self(), vx, vy, vz])
|
||||
|
||||
@property
|
||||
def enable_append_object_velocity(self):
|
||||
libfunc = lib.MeshFluidSource_is_append_object_velocity_enabled
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_int)
|
||||
return bool(pb.execute_lib_func(libfunc, [self()]))
|
||||
|
||||
@enable_append_object_velocity.setter
|
||||
def enable_append_object_velocity(self, boolval):
|
||||
if boolval:
|
||||
libfunc = lib.MeshFluidSource_enable_append_object_velocity
|
||||
else:
|
||||
libfunc = lib.MeshFluidSource_disable_append_object_velocity
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@property
|
||||
def object_velocity_influence(self):
|
||||
libfunc = lib.MeshFluidSource_get_object_velocity_influence
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_float)
|
||||
return pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@object_velocity_influence.setter
|
||||
def object_velocity_influence(self, value):
|
||||
libfunc = lib.MeshFluidSource_set_object_velocity_influence
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_float, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self(), value])
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
libfunc = lib.MeshFluidSource_get_priority
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_int)
|
||||
return pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@priority.setter
|
||||
def priority(self, n):
|
||||
libfunc = lib.MeshFluidSource_set_priority
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_int, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self(), int(n)])
|
||||
|
||||
@property
|
||||
def enable_constrained_fluid_velocity(self):
|
||||
libfunc = lib.MeshFluidSource_is_constrained_fluid_velocity_enabled
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_int)
|
||||
return bool(pb.execute_lib_func(libfunc, [self()]))
|
||||
|
||||
@enable_constrained_fluid_velocity.setter
|
||||
def enable_constrained_fluid_velocity(self, boolval):
|
||||
if boolval:
|
||||
libfunc = lib.MeshFluidSource_enable_constrained_fluid_velocity
|
||||
else:
|
||||
libfunc = lib.MeshFluidSource_disable_constrained_fluid_velocity
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@property
|
||||
def outflow_inverse(self):
|
||||
libfunc = lib.MeshFluidSource_is_outflow_inversed
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_int)
|
||||
return bool(pb.execute_lib_func(libfunc, [self()]))
|
||||
|
||||
@outflow_inverse.setter
|
||||
def outflow_inverse(self, boolval):
|
||||
do_inverse = (boolval and not self.outflow_inverse) or (not boolval and self.outflow_inverse)
|
||||
if do_inverse:
|
||||
libfunc = lib.MeshFluidSource_outflow_inverse
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@property
|
||||
def source_id(self):
|
||||
libfunc = lib.MeshFluidSource_get_source_id
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_int)
|
||||
return pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@source_id.setter
|
||||
def source_id(self, n):
|
||||
libfunc = lib.MeshFluidSource_set_source_id
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_int, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self(), int(n)])
|
||||
|
||||
@property
|
||||
def viscosity(self):
|
||||
libfunc = lib.MeshFluidSource_get_viscosity
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_int)
|
||||
return pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@viscosity.setter
|
||||
def viscosity(self, v):
|
||||
libfunc = lib.MeshFluidSource_set_viscosity
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_float, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self(), float(v)])
|
||||
|
||||
@property
|
||||
def lifetime(self):
|
||||
libfunc = lib.MeshFluidSource_get_lifetime
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_int)
|
||||
return pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@lifetime.setter
|
||||
def lifetime(self, v):
|
||||
libfunc = lib.MeshFluidSource_set_lifetime
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_float, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self(), float(v)])
|
||||
|
||||
@property
|
||||
def lifetime_variance(self):
|
||||
libfunc = lib.MeshFluidSource_get_lifetime_variance
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_int)
|
||||
return pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@lifetime_variance.setter
|
||||
def lifetime_variance(self, v):
|
||||
libfunc = lib.MeshFluidSource_set_lifetime_variance
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_float, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self(), float(v)])
|
||||
|
||||
def get_source_color(self):
|
||||
libfunc = lib.MeshFluidSource_get_source_color
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], Vector3_t)
|
||||
cvect = pb.execute_lib_func(libfunc, [self()])
|
||||
return Vector3.from_struct(cvect)
|
||||
|
||||
@decorators.xyz_or_vector
|
||||
def set_source_color(self, r, g, b):
|
||||
libfunc = lib.MeshFluidSource_set_source_color
|
||||
pb.init_lib_func(
|
||||
libfunc,
|
||||
[c_void_p, c_double, c_double, c_double, c_void_p], None
|
||||
)
|
||||
pb.execute_lib_func(libfunc, [self(), r, g, b])
|
||||
@@ -0,0 +1,273 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
from ctypes import c_void_p, c_char_p, c_int, c_float, c_double, byref
|
||||
|
||||
from .ffengine import ffengine as lib
|
||||
from . import pybindings as pb
|
||||
from . import method_decorators as decorators
|
||||
from .trianglemesh import TriangleMesh_t
|
||||
|
||||
class MeshObject():
|
||||
|
||||
def __init__(self, i, j, k, dx):
|
||||
libfunc = lib.MeshObject_new
|
||||
args = [c_int, c_int, c_int, c_double, c_void_p]
|
||||
pb.init_lib_func(libfunc, args, c_void_p)
|
||||
self._obj = pb.execute_lib_func(libfunc, [i, j, k, dx])
|
||||
|
||||
def __del__(self):
|
||||
try:
|
||||
libfunc = lib.MeshObject_destroy
|
||||
pb.init_lib_func(libfunc, [c_void_p], None)
|
||||
libfunc(self._obj)
|
||||
except:
|
||||
pass
|
||||
|
||||
def __call__(self):
|
||||
return self._obj
|
||||
|
||||
def update_mesh_static(self, mesh):
|
||||
mesh_struct = mesh.to_struct()
|
||||
libfunc = lib.MeshObject_update_mesh_static
|
||||
args = [c_void_p, TriangleMesh_t, c_void_p]
|
||||
pb.init_lib_func(libfunc, args, c_void_p)
|
||||
pb.execute_lib_func(libfunc, [self(), mesh_struct])
|
||||
|
||||
def update_mesh_animated(self, mesh_previous, mesh_current, mesh_next):
|
||||
mesh_struct_previous = mesh_previous.to_struct()
|
||||
mesh_struct_current = mesh_current.to_struct()
|
||||
mesh_struct_next = mesh_next.to_struct()
|
||||
libfunc = lib.MeshObject_update_mesh_animated
|
||||
args = [c_void_p, TriangleMesh_t, TriangleMesh_t, TriangleMesh_t, c_void_p]
|
||||
pb.init_lib_func(libfunc, args, c_void_p)
|
||||
pb.execute_lib_func(libfunc, [self(), mesh_struct_previous,
|
||||
mesh_struct_current,
|
||||
mesh_struct_next])
|
||||
|
||||
@property
|
||||
def enable(self):
|
||||
libfunc = lib.MeshObject_is_enabled
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_int)
|
||||
return bool(pb.execute_lib_func(libfunc, [self()]))
|
||||
|
||||
@enable.setter
|
||||
def enable(self, boolval):
|
||||
if boolval:
|
||||
libfunc = lib.MeshObject_enable
|
||||
else:
|
||||
libfunc = lib.MeshObject_disable
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@property
|
||||
def inverse(self):
|
||||
libfunc = lib.MeshObject_is_inversed
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_int)
|
||||
return bool(pb.execute_lib_func(libfunc, [self()]))
|
||||
|
||||
@inverse.setter
|
||||
def inverse(self, boolval):
|
||||
do_inverse = (boolval and not self.inverse) or (not boolval and self.inverse)
|
||||
if do_inverse:
|
||||
libfunc = lib.MeshObject_inverse
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@property
|
||||
def friction(self):
|
||||
libfunc = lib.MeshObject_get_friction
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_float)
|
||||
return pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@friction.setter
|
||||
@decorators.check_ge_zero
|
||||
@decorators.check_le(1.0)
|
||||
def friction(self, value):
|
||||
libfunc = lib.MeshObject_set_friction
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_float, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self(), value])
|
||||
|
||||
@property
|
||||
def velocity_scale(self):
|
||||
libfunc = lib.MeshObject_get_velocity_scale
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_float)
|
||||
return pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@velocity_scale.setter
|
||||
def velocity_scale(self, value):
|
||||
libfunc = lib.MeshObject_set_velocity_scale
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_float, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self(), value])
|
||||
|
||||
@property
|
||||
def whitewater_influence(self):
|
||||
libfunc = lib.MeshObject_get_whitewater_influence
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_float)
|
||||
return pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@whitewater_influence.setter
|
||||
@decorators.check_ge_zero
|
||||
def whitewater_influence(self, value):
|
||||
libfunc = lib.MeshObject_set_whitewater_influence
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_float, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self(), value])
|
||||
|
||||
@property
|
||||
def dust_emission_strength(self):
|
||||
libfunc = lib.MeshObject_get_dust_emission_strength
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_float)
|
||||
return pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@dust_emission_strength.setter
|
||||
@decorators.check_ge_zero
|
||||
def dust_emission_strength(self, value):
|
||||
libfunc = lib.MeshObject_set_dust_emission_strength
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_float, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self(), value])
|
||||
|
||||
@property
|
||||
def sheeting_strength(self):
|
||||
libfunc = lib.MeshObject_get_sheeting_strength
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_float)
|
||||
return pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@sheeting_strength.setter
|
||||
@decorators.check_ge_zero
|
||||
def sheeting_strength(self, value):
|
||||
libfunc = lib.MeshObject_set_sheeting_strength
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_float, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self(), value])
|
||||
|
||||
@property
|
||||
def mesh_expansion(self):
|
||||
libfunc = lib.MeshObject_get_mesh_expansion
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_float)
|
||||
return pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@mesh_expansion.setter
|
||||
def mesh_expansion(self, value):
|
||||
libfunc = lib.MeshObject_set_mesh_expansion
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_float, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self(), value])
|
||||
|
||||
@property
|
||||
def enable_append_object_velocity(self):
|
||||
libfunc = lib.MeshObject_is_append_object_velocity_enabled
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_int)
|
||||
return bool(pb.execute_lib_func(libfunc, [self()]))
|
||||
|
||||
@enable_append_object_velocity.setter
|
||||
def enable_append_object_velocity(self, boolval):
|
||||
if boolval:
|
||||
libfunc = lib.MeshObject_enable_append_object_velocity
|
||||
else:
|
||||
libfunc = lib.MeshObject_disable_append_object_velocity
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@property
|
||||
def object_velocity_influence(self):
|
||||
libfunc = lib.MeshObject_get_object_velocity_influence
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_float)
|
||||
return pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@object_velocity_influence.setter
|
||||
def object_velocity_influence(self, value):
|
||||
libfunc = lib.MeshObject_set_object_velocity_influence
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_float, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self(), value])
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
libfunc = lib.MeshObject_get_priority
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_int)
|
||||
return pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@priority.setter
|
||||
def priority(self, n):
|
||||
libfunc = lib.MeshObject_set_priority
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_int, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self(), int(n)])
|
||||
|
||||
@property
|
||||
def source_id(self):
|
||||
libfunc = lib.MeshObject_get_source_id
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_int)
|
||||
return pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@source_id.setter
|
||||
def source_id(self, n):
|
||||
libfunc = lib.MeshObject_set_source_id
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_int, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self(), int(n)])
|
||||
|
||||
@property
|
||||
def viscosity(self):
|
||||
libfunc = lib.MeshObject_get_viscosity
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_int)
|
||||
return pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@viscosity.setter
|
||||
def viscosity(self, v):
|
||||
libfunc = lib.MeshObject_set_viscosity
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_float, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self(), float(v)])
|
||||
|
||||
@property
|
||||
def lifetime(self):
|
||||
libfunc = lib.MeshObject_get_lifetime
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_int)
|
||||
return pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@lifetime.setter
|
||||
def lifetime(self, v):
|
||||
libfunc = lib.MeshObject_set_lifetime
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_float, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self(), float(v)])
|
||||
|
||||
@property
|
||||
def lifetime_variance(self):
|
||||
libfunc = lib.MeshObject_get_lifetime_variance
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], c_int)
|
||||
return pb.execute_lib_func(libfunc, [self()])
|
||||
|
||||
@lifetime_variance.setter
|
||||
def lifetime_variance(self, v):
|
||||
libfunc = lib.MeshObject_set_lifetime_variance
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_float, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [self(), float(v)])
|
||||
|
||||
def get_source_color(self):
|
||||
libfunc = lib.MeshObject_get_source_color
|
||||
pb.init_lib_func(libfunc, [c_void_p, c_void_p], Vector3_t)
|
||||
cvect = pb.execute_lib_func(libfunc, [self()])
|
||||
return Vector3.from_struct(cvect)
|
||||
|
||||
@decorators.xyz_or_vector
|
||||
def set_source_color(self, r, g, b):
|
||||
libfunc = lib.MeshObject_set_source_color
|
||||
pb.init_lib_func(
|
||||
libfunc,
|
||||
[c_void_p, c_double, c_double, c_double, c_void_p], None
|
||||
)
|
||||
pb.execute_lib_func(libfunc, [self(), r, g, b])
|
||||
@@ -0,0 +1,127 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import numbers
|
||||
|
||||
from .vector3 import Vector3
|
||||
from .gridindex import GridIndex
|
||||
|
||||
def ijk_or_gridindex(func):
|
||||
def ijk_or_gridindex_wrapper(self, *args):
|
||||
try:
|
||||
i, j, k = args
|
||||
except:
|
||||
i, j, k = args[0]
|
||||
return func(self, i, j, k)
|
||||
return ijk_or_gridindex_wrapper
|
||||
|
||||
def ijk_or_gridindex_and_value(func):
|
||||
def ijk_or_gridindex_and_value_wrapper(self, *args):
|
||||
try:
|
||||
return func(self, *args)
|
||||
except:
|
||||
i, j, k = args[0]
|
||||
return func(self, i, j, k, args[1])
|
||||
return ijk_or_gridindex_and_value_wrapper
|
||||
|
||||
def xyz_or_vector(func):
|
||||
def xyz_or_vector_wrapper(self, *args):
|
||||
try:
|
||||
return func(self, *args)
|
||||
except:
|
||||
return func(self, *args[0])
|
||||
return xyz_or_vector_wrapper
|
||||
|
||||
def xyz_or_vector_and_radius(func):
|
||||
def xyz_or_vector_wrapper(self, *args):
|
||||
try:
|
||||
return func(self, *args)
|
||||
except:
|
||||
x, y, z = args[0]
|
||||
return func(self, x, y, z, args[1])
|
||||
return xyz_or_vector_wrapper
|
||||
|
||||
def check_gt_zero(func):
|
||||
def check_values(self, *args):
|
||||
for arg in args:
|
||||
if isinstance(arg, numbers.Real) and arg <= 0:
|
||||
raise ValueError("Value must be greater than zero")
|
||||
return func(self, *args)
|
||||
return check_values
|
||||
|
||||
def check_ge_zero(func):
|
||||
def check_values(self, *args):
|
||||
for arg in args:
|
||||
if isinstance(arg, numbers.Real) and arg < 0:
|
||||
raise ValueError("Value must be greater than or equal to zero")
|
||||
return func(self, *args)
|
||||
return check_values
|
||||
|
||||
def check_gt(value):
|
||||
def check_gt_decorator(func):
|
||||
def check_gt_wrapper(self, *args):
|
||||
for arg in args:
|
||||
if isinstance(arg, numbers.Real) and arg <= value:
|
||||
raise ValueError("Value must be greater than " + str(value))
|
||||
return func(self, *args)
|
||||
return check_gt_wrapper
|
||||
return check_gt_decorator
|
||||
|
||||
def check_ge(value):
|
||||
def check_ge_decorator(func):
|
||||
def check_ge_wrapper(self, *args):
|
||||
for arg in args:
|
||||
if isinstance(arg, numbers.Real) and arg < value:
|
||||
raise ValueError("Value must be greater than or equal to " + str(value))
|
||||
return func(self, *args)
|
||||
return check_ge_wrapper
|
||||
return check_ge_decorator
|
||||
|
||||
def check_lt(value):
|
||||
def check_lt_decorator(func):
|
||||
def check_lt_wrapper(self, *args):
|
||||
for arg in args:
|
||||
if isinstance(arg, numbers.Real) and arg >= value:
|
||||
raise ValueError("Value must be less than " + str(value))
|
||||
return func(self, *args)
|
||||
return check_lt_wrapper
|
||||
return check_lt_decorator
|
||||
|
||||
def check_le(value):
|
||||
def check_le_decorator(func):
|
||||
def check_le_wrapper(self, *args):
|
||||
for arg in args:
|
||||
if isinstance(arg, numbers.Real) and arg > value:
|
||||
raise ValueError("Value must be less than or equal to " + str(value))
|
||||
return func(self, *args)
|
||||
return check_le_wrapper
|
||||
return check_le_decorator
|
||||
|
||||
def check_type(argtype):
|
||||
def check_type_decorator(func):
|
||||
def check_type_wrapper(self, *args):
|
||||
for arg in args:
|
||||
if not isinstance(arg, argtype):
|
||||
raise TypeError("Argument must be of type " + str(argtype))
|
||||
return func(self, *args)
|
||||
return check_type_wrapper
|
||||
return check_type_decorator
|
||||
@@ -0,0 +1,58 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import ctypes
|
||||
from ctypes import c_void_p, c_char_p, c_char, c_int, c_uint, c_float, c_double, byref
|
||||
|
||||
from .ffengine import ffengine as lib
|
||||
from .vector3 import Vector3, Vector3_t
|
||||
from . import pybindings as pb
|
||||
|
||||
|
||||
class MixboxLutData_t(ctypes.Structure):
|
||||
_fields_ = [("size", c_int),
|
||||
("data", c_char_p)]
|
||||
|
||||
|
||||
def initialize(lut_data, lut_data_size):
|
||||
c_lut_data = (c_char * len(lut_data)).from_buffer_copy(lut_data)
|
||||
|
||||
mb_data = MixboxLutData_t()
|
||||
mb_data.size = lut_data_size
|
||||
mb_data.data = ctypes.cast(c_lut_data, c_char_p)
|
||||
|
||||
libfunc = lib.Mixbox_initialize
|
||||
pb.init_lib_func(libfunc, [MixboxLutData_t, c_void_p], None)
|
||||
pb.execute_lib_func(libfunc, [mb_data])
|
||||
|
||||
|
||||
def is_initialized():
|
||||
libfunc = lib.Mixbox_is_initialized
|
||||
pb.init_lib_func(libfunc, [c_void_p], c_int)
|
||||
return bool(pb.execute_lib_func(libfunc, []))
|
||||
|
||||
|
||||
def lerp_srgb32f(r1, g1, b1, r2, g2, b2, t):
|
||||
libfunc = lib.Mixbox_lerp_srgb32f
|
||||
pb.init_lib_func(libfunc, [c_float, c_float, c_float, c_float, c_float, c_float, c_float, c_void_p], Vector3_t)
|
||||
cvect = pb.execute_lib_func(libfunc, [r1, g1, b1, r2, g2, b2, t])
|
||||
return cvect.x, cvect.y, cvect.z
|
||||
@@ -0,0 +1,60 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
from .ffengine import ffengine as lib
|
||||
from ctypes import c_char_p, c_int, byref
|
||||
|
||||
def check_success(success, errprefix):
|
||||
libfunc = lib.CBindings_get_error_message
|
||||
init_lib_func(libfunc, [], c_char_p)
|
||||
if not success:
|
||||
raise RuntimeError(errprefix + str(libfunc().decode("utf-8")))
|
||||
|
||||
def init_lib_func(libfunc, argtypes, restype):
|
||||
if libfunc.argtypes is None:
|
||||
libfunc.argtypes = argtypes
|
||||
libfunc.restype = restype
|
||||
|
||||
def execute_lib_func(libfunc, params):
|
||||
args = []
|
||||
for idx, arg in enumerate(params):
|
||||
try:
|
||||
cval = libfunc.argtypes[idx](arg)
|
||||
except:
|
||||
cval = arg
|
||||
args.append(cval)
|
||||
success = c_int();
|
||||
args.append(byref(success))
|
||||
|
||||
result = None
|
||||
if libfunc.restype:
|
||||
funcresult = libfunc(*args)
|
||||
check_success(success, libfunc.__name__ + " - ")
|
||||
try:
|
||||
return libfunc.restype(funcresult).value
|
||||
except:
|
||||
return funcresult
|
||||
else:
|
||||
libfunc(*args)
|
||||
|
||||
check_success(success, libfunc.__name__ + " - ")
|
||||
return result
|
||||
@@ -0,0 +1,113 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import ctypes
|
||||
import array
|
||||
import struct
|
||||
|
||||
class TriangleMesh_t(ctypes.Structure):
|
||||
_fields_ = [("vertices", ctypes.c_void_p),
|
||||
("triangles", ctypes.c_void_p),
|
||||
("num_vertices", ctypes.c_int),
|
||||
("num_triangles", ctypes.c_int)]
|
||||
|
||||
class TriangleMesh(object):
|
||||
def __init__(self):
|
||||
self.vertices = array.array('f', [])
|
||||
self.triangles = array.array('i', [])
|
||||
|
||||
|
||||
@classmethod
|
||||
def from_bobj(cls, bobj_data):
|
||||
int_data, bobj_data = bobj_data[:4], bobj_data[4:]
|
||||
num_vertices = struct.unpack('i', int_data)[0]
|
||||
|
||||
num_floats = 3 * num_vertices
|
||||
num_bytes = 4 * num_floats
|
||||
vertex_data, bobj_data = bobj_data[:num_bytes], bobj_data[num_bytes:]
|
||||
vertices = list(struct.unpack('{0}f'.format(num_floats), vertex_data))
|
||||
|
||||
int_data, bobj_data = bobj_data[:4], bobj_data[4:]
|
||||
num_triangles = struct.unpack('i', int_data)[0]
|
||||
|
||||
num_ints = 3 * num_triangles
|
||||
num_bytes = 4 * num_ints
|
||||
triangle_data, bobj_data = bobj_data[:num_bytes], bobj_data[num_bytes:]
|
||||
triangles = list(struct.unpack('{0}i'.format(num_ints), triangle_data))
|
||||
|
||||
self = cls()
|
||||
self.vertices = array.array('f', vertices)
|
||||
self.triangles = array.array('i', triangles)
|
||||
|
||||
return self
|
||||
|
||||
|
||||
def to_bobj(self):
|
||||
num_vertices = len(self.vertices) // 3
|
||||
num_triangles = len(self.triangles) // 3
|
||||
datastr = struct.pack('i', num_vertices)
|
||||
datastr += self.vertices.tobytes()
|
||||
datastr += struct.pack('i', num_triangles)
|
||||
datastr += self.triangles.tobytes()
|
||||
|
||||
return datastr
|
||||
|
||||
def to_struct(self):
|
||||
|
||||
num_vertices = len(self.vertices) // 3
|
||||
num_triangles = len(self.triangles) // 3
|
||||
|
||||
vertex_data = (ctypes.c_float * len(self.vertices))()
|
||||
for i in range(len(self.vertices)):
|
||||
vertex_data[i] = self.vertices[i]
|
||||
|
||||
triangle_data = (ctypes.c_int * len(self.triangles))()
|
||||
for i in range(len(self.triangles)):
|
||||
triangle_data[i] = self.triangles[i]
|
||||
|
||||
struct = TriangleMesh_t()
|
||||
struct.vertices = ctypes.cast(vertex_data, ctypes.c_void_p)
|
||||
struct.triangles = ctypes.cast(triangle_data, ctypes.c_void_p)
|
||||
struct.num_vertices = num_vertices
|
||||
struct.num_triangles = num_triangles
|
||||
|
||||
return struct
|
||||
|
||||
def apply_transform(self, matrix_world):
|
||||
m = matrix_world
|
||||
for i in range(0, len(self.vertices), 3):
|
||||
v = [self.vertices[i + 0], self.vertices[i + 1], self.vertices[i + 2], 1]
|
||||
self.vertices[i + 0] = v[0]*m[0] + v[1]*m[1] + v[2]*m[2] + v[3]*m[3]
|
||||
self.vertices[i + 1] = v[0]*m[4] + v[1]*m[5] + v[2]*m[6] + v[3]*m[7]
|
||||
self.vertices[i + 2] = v[0]*m[8] + v[1]*m[9] + v[2]*m[10] + v[3]*m[11]
|
||||
|
||||
def translate(self, tx, ty, tz):
|
||||
for i in range(0, len(self.vertices), 3):
|
||||
self.vertices[i + 0] += tx
|
||||
self.vertices[i + 1] += ty
|
||||
self.vertices[i + 2] += tz
|
||||
|
||||
def scale(self, scale):
|
||||
for i in range(0, len(self.vertices), 3):
|
||||
self.vertices[i + 0] *= scale
|
||||
self.vertices[i + 1] *= scale
|
||||
self.vertices[i + 2] *= scale
|
||||
@@ -0,0 +1,217 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import array
|
||||
import ctypes
|
||||
import math
|
||||
|
||||
class Vector3_t(ctypes.Structure):
|
||||
_fields_ = [("x", ctypes.c_float),
|
||||
("y", ctypes.c_float),
|
||||
("z", ctypes.c_float)]
|
||||
|
||||
class Vector3(object):
|
||||
|
||||
def __init__(self, x = 0.0, y = 0.0, z = 0.0):
|
||||
if isinstance(x, Vector3):
|
||||
self._values = array.array('f', [x.x, x.y, x.z])
|
||||
else:
|
||||
self._values = array.array('f', [x, y, z])
|
||||
|
||||
@classmethod
|
||||
def from_struct(cls, cstruct):
|
||||
return cls(cstruct.x, cstruct.y, cstruct.z)
|
||||
|
||||
def to_struct(self):
|
||||
return Vector3_t(self.x, self.y, self.z)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.x) + " " + str(self.y) + " " + str(self.z)
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key < 0 or key > 2:
|
||||
raise IndexError("Index must be in range [0, 2]")
|
||||
if not isinstance(key, int):
|
||||
raise TypeError("Index must be an integer")
|
||||
|
||||
return self._values[key]
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
if key < 0 or key > 2:
|
||||
raise IndexError("Index must be in range [0, 2]")
|
||||
if not isinstance(key, int):
|
||||
raise TypeError("Index must be an integer")
|
||||
|
||||
self._values[key] = value
|
||||
|
||||
def __iter__(self):
|
||||
yield self._values[0]
|
||||
yield self._values[1]
|
||||
yield self._values[2]
|
||||
|
||||
def __add__(self, other):
|
||||
return Vector3(self.x + other.x,
|
||||
self.y + other.y,
|
||||
self.z + other.z)
|
||||
|
||||
def __iadd__(self, other):
|
||||
self.x += other.x
|
||||
self.y += other.y
|
||||
self.z += other.z
|
||||
return self
|
||||
|
||||
def __sub__(self, other):
|
||||
return Vector3(self.x - other.x,
|
||||
self.y - other.y,
|
||||
self.z - other.z)
|
||||
|
||||
def __isub__(self, other):
|
||||
self.x -= other.x
|
||||
self.y -= other.y
|
||||
self.z -= other.z
|
||||
return self
|
||||
|
||||
def __mul__(self, scale):
|
||||
return Vector3(scale * self.x,
|
||||
scale * self.y,
|
||||
scale * self.z)
|
||||
|
||||
def __imul__(self, scale):
|
||||
self.x *= scale
|
||||
self.y *= scale
|
||||
self.z *= scale
|
||||
return self
|
||||
|
||||
def __rmul__(self, scale):
|
||||
return Vector3(scale * self.x,
|
||||
scale * self.y,
|
||||
scale * self.z)
|
||||
|
||||
def __div__(self, denominator):
|
||||
if denominator == 0.0:
|
||||
raise ZeroDivisionError
|
||||
|
||||
inv = 1.0 / denominator
|
||||
|
||||
return Vector3(self.x * inv,
|
||||
self.y * inv,
|
||||
self.z * inv)
|
||||
|
||||
def __idiv__(self, denominator):
|
||||
if denominator == 0.0:
|
||||
raise ZeroDivisionError
|
||||
|
||||
inv = 1.0 / denominator
|
||||
|
||||
self.x *= inv
|
||||
self.y *= inv
|
||||
self.z *= inv
|
||||
return self
|
||||
|
||||
def __neg__(self):
|
||||
return Vector3(-self.x, -self.y, -self.z)
|
||||
|
||||
def __pos__(self):
|
||||
return Vector3(self)
|
||||
|
||||
def __abs__(self):
|
||||
return Vector3(abs(self.x), abs(self.y), abs(self.z))
|
||||
|
||||
def __invert__(self):
|
||||
return Vector3(1.0 / self.x,
|
||||
1.0 / self.y,
|
||||
1.0 / self.z)
|
||||
|
||||
@property
|
||||
def x(self):
|
||||
return self._values[0]
|
||||
|
||||
@property
|
||||
def y(self):
|
||||
return self._values[1]
|
||||
|
||||
@property
|
||||
def z(self):
|
||||
return self._values[2]
|
||||
|
||||
@x.setter
|
||||
def x(self, value):
|
||||
self._values[0] = value
|
||||
|
||||
@y.setter
|
||||
def y(self, value):
|
||||
self._values[1] = value
|
||||
|
||||
@z.setter
|
||||
def z(self, value):
|
||||
self._values[2] = value
|
||||
|
||||
def add(self, vector):
|
||||
self += vector
|
||||
return self
|
||||
|
||||
def sub(self, vector):
|
||||
self -= vector
|
||||
return self
|
||||
|
||||
def mult(self, scale):
|
||||
self *= scale
|
||||
return self
|
||||
|
||||
def div(self, denominator):
|
||||
self /= denominator
|
||||
return self
|
||||
|
||||
def neg(self):
|
||||
self.x = -self.x
|
||||
self.y = -self.y
|
||||
self.z = -self.z
|
||||
return self
|
||||
|
||||
def invert(self):
|
||||
self.x = 1.0 / self.x
|
||||
self.y = 1.0 / self.y
|
||||
self.z = 1.0 / self.z
|
||||
return self
|
||||
|
||||
def dot(vector):
|
||||
return self.x*vector.x + self.y*vector.y + self.z*vector.z
|
||||
|
||||
def cross(vector):
|
||||
return Vector3(self.y*vector.z - self.z*vector.y,
|
||||
self.z*vector.x - self.x*vector.z,
|
||||
self.x*vector.y - self.y*vector.x)
|
||||
|
||||
def lengthsq(self):
|
||||
return self.x*self.x + self.y*self.y + self.z*self.z
|
||||
|
||||
def length(self):
|
||||
return math.sqrt(self.x*self.x + self.y*self.y + self.z*self.z)
|
||||
|
||||
def normalize(self):
|
||||
length = self.length()
|
||||
if length == 0.0:
|
||||
raise ZeroDivisionError
|
||||
|
||||
inv = 1.0 / length
|
||||
self *= inv
|
||||
return self
|
||||
Reference in New Issue
Block a user