fractex 0.1.2__tar.gz → 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fractex-0.1.2/fractex.egg-info → fractex-0.2.1}/PKG-INFO +2 -1
- {fractex-0.1.2 → fractex-0.2.1}/fractex/3d.py +28 -7
- {fractex-0.1.2 → fractex-0.2.1}/fractex/cli.py +1 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/3d.py +2 -2
- {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/__init__.py +1 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/architecture_pattern.py +1 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/composite_material.py +1 -0
- fractex-0.2.1/fractex/examples/fire_flame.py +141 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/splash.py +56 -39
- {fractex-0.1.2 → fractex-0.2.1/fractex.egg-info}/PKG-INFO +2 -1
- {fractex-0.1.2 → fractex-0.2.1}/fractex.egg-info/SOURCES.txt +1 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex.egg-info/requires.txt +1 -0
- {fractex-0.1.2 → fractex-0.2.1}/pyproject.toml +2 -2
- {fractex-0.1.2 → fractex-0.2.1}/LICENSE +0 -0
- {fractex-0.1.2 → fractex-0.2.1}/MANIFEST.in +0 -0
- {fractex-0.1.2 → fractex-0.2.1}/README.md +0 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex/__init__.py +0 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex/advanced.py +0 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex/core.py +0 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex/dynamic_textures_3d.py +0 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/3d_integration.py +0 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/3d_integration_2d.py +0 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/_output.py +0 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/atmosphere.py +0 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/crystal_cave.py +0 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/custom_pattern.py +0 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/game_integration.py +0 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/game_texture.py +0 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/integration.py +0 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/physic_integration.py +0 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/terrain.py +0 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/underwater.py +0 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/underwater_volkano.py +0 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex/geometric_patterns_3d.py +0 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex/interactive.py +0 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex/simplex_noise.py +0 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex/texture_blending.py +0 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex/volume_scattering.py +0 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex/volume_textures.py +0 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex.egg-info/dependency_links.txt +0 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex.egg-info/entry_points.txt +0 -0
- {fractex-0.1.2 → fractex-0.2.1}/fractex.egg-info/top_level.txt +0 -0
- {fractex-0.1.2 → fractex-0.2.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fractex
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Fractal texture engine for procedural and infinite-detail textures.
|
|
5
5
|
Author-email: Timur Isanov <tisanov@yahoo.com>
|
|
6
6
|
License: MIT License
|
|
@@ -43,6 +43,7 @@ Description-Content-Type: text/markdown
|
|
|
43
43
|
License-File: LICENSE
|
|
44
44
|
Requires-Dist: numpy
|
|
45
45
|
Requires-Dist: numba
|
|
46
|
+
Requires-Dist: matplotlib
|
|
46
47
|
Dynamic: license-file
|
|
47
48
|
|
|
48
49
|
## Fractex
|
|
@@ -155,6 +155,9 @@ def sample_volume_trilinear(volume: np.ndarray,
|
|
|
155
155
|
Интерполированное значение (C,)
|
|
156
156
|
"""
|
|
157
157
|
depth, height, width, channels = volume.shape
|
|
158
|
+
x = np.float32(x)
|
|
159
|
+
y = np.float32(y)
|
|
160
|
+
z = np.float32(z)
|
|
158
161
|
|
|
159
162
|
# Обрабатываем режим заворачивания
|
|
160
163
|
if wrap_mode == "repeat":
|
|
@@ -162,17 +165,35 @@ def sample_volume_trilinear(volume: np.ndarray,
|
|
|
162
165
|
y = y - np.floor(y)
|
|
163
166
|
z = z - np.floor(z)
|
|
164
167
|
elif wrap_mode == "clamp":
|
|
165
|
-
x
|
|
166
|
-
|
|
167
|
-
|
|
168
|
+
if x < 0.0:
|
|
169
|
+
x = 0.0
|
|
170
|
+
elif x > 1.0:
|
|
171
|
+
x = 1.0
|
|
172
|
+
if y < 0.0:
|
|
173
|
+
y = 0.0
|
|
174
|
+
elif y > 1.0:
|
|
175
|
+
y = 1.0
|
|
176
|
+
if z < 0.0:
|
|
177
|
+
z = 0.0
|
|
178
|
+
elif z > 1.0:
|
|
179
|
+
z = 1.0
|
|
168
180
|
elif wrap_mode == "mirror":
|
|
169
181
|
x = np.abs(1.0 - np.abs(1.0 - x * 2.0)) / 2.0
|
|
170
182
|
y = np.abs(1.0 - np.abs(1.0 - y * 2.0)) / 2.0
|
|
171
183
|
z = np.abs(1.0 - np.abs(1.0 - z * 2.0)) / 2.0
|
|
172
184
|
else:
|
|
173
|
-
x
|
|
174
|
-
|
|
175
|
-
|
|
185
|
+
if x < 0.0:
|
|
186
|
+
x = 0.0
|
|
187
|
+
elif x > 1.0:
|
|
188
|
+
x = 1.0
|
|
189
|
+
if y < 0.0:
|
|
190
|
+
y = 0.0
|
|
191
|
+
elif y > 1.0:
|
|
192
|
+
y = 1.0
|
|
193
|
+
if z < 0.0:
|
|
194
|
+
z = 0.0
|
|
195
|
+
elif z > 1.0:
|
|
196
|
+
z = 1.0
|
|
176
197
|
|
|
177
198
|
# Переводим в координаты текстуры
|
|
178
199
|
fx = x * (width - 1)
|
|
@@ -221,7 +242,7 @@ def sample_volume_trilinear(volume: np.ndarray,
|
|
|
221
242
|
# Линейная интерполяция по Z
|
|
222
243
|
result = c0 * (1 - dz) + c1 * dz
|
|
223
244
|
|
|
224
|
-
return result
|
|
245
|
+
return result.astype(volume.dtype)
|
|
225
246
|
|
|
226
247
|
@jit(nopython=True, parallel=True, cache=True)
|
|
227
248
|
def sample_volume_trilinear_batch(volume: np.ndarray,
|
|
@@ -34,7 +34,7 @@ def main():
|
|
|
34
34
|
args = parser.parse_args()
|
|
35
35
|
game_time = 0.0
|
|
36
36
|
player = Player(
|
|
37
|
-
position=np.array([0.5, 0.5,
|
|
37
|
+
position=np.array([0.5, 0.5, 0.9]),
|
|
38
38
|
look_at=np.array([0.5, 0.5, 0.5])
|
|
39
39
|
)
|
|
40
40
|
|
|
@@ -73,7 +73,7 @@ def main():
|
|
|
73
73
|
# Рендеринг
|
|
74
74
|
renderer = VolumeTextureRenderer(cave_with_lava)
|
|
75
75
|
frames = []
|
|
76
|
-
for z in [
|
|
76
|
+
for z in [0.95, 0.85, 0.75, 0.65, 0.55, 0.45]:
|
|
77
77
|
camera_pos = np.array([0.5, 0.5, z])
|
|
78
78
|
frame = renderer.render_raycast(
|
|
79
79
|
camera_pos=camera_pos,
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import argparse
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
ROOT = Path(__file__).resolve().parents[2]
|
|
7
|
+
if str(ROOT) not in sys.path:
|
|
8
|
+
sys.path.insert(0, str(ROOT))
|
|
9
|
+
EXAMPLES_DIR = Path(__file__).resolve().parent
|
|
10
|
+
if str(EXAMPLES_DIR) not in sys.path:
|
|
11
|
+
sys.path.insert(0, str(EXAMPLES_DIR))
|
|
12
|
+
|
|
13
|
+
from fractex.simplex_noise import SimplexNoise
|
|
14
|
+
from fractex.interactive import add_interactive_args, InteractiveConfig, run_interactive
|
|
15
|
+
from _output import save_ppm
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _render_flame(t: float, w: int, h: int, noise: SimplexNoise) -> np.ndarray:
|
|
19
|
+
x = np.linspace(-1.0, 1.0, w)
|
|
20
|
+
y = np.linspace(0.0, 2.0, h)
|
|
21
|
+
xx, yy = np.meshgrid(x, y)
|
|
22
|
+
taper = 1.0 - 0.5 * np.clip(yy / 2.0, 0, 1)
|
|
23
|
+
xx_t = xx * taper
|
|
24
|
+
zz = np.ones_like(xx) * (t * 0.15)
|
|
25
|
+
|
|
26
|
+
base = noise.noise_3d(xx_t * 1.5, yy * 1.2, zz)
|
|
27
|
+
detail = noise.noise_3d(xx_t * 5.0, yy * 4.0, zz * 1.8)
|
|
28
|
+
micro = noise.noise_3d(xx_t * 14.0, yy * 10.0, zz * 5.0)
|
|
29
|
+
warp = noise.noise_3d(xx_t * 1.2, yy * 1.0, zz * 0.8)
|
|
30
|
+
turbulence = base * 0.45 + detail * 0.3 + micro * 0.2 + warp * 0.05
|
|
31
|
+
turbulence = (turbulence + 1) * 0.5
|
|
32
|
+
|
|
33
|
+
# Центральное ядро + вытянутая форма вверх (с повышенным контрастом)
|
|
34
|
+
core = np.exp(-((xx_t * 1.8) ** 2)) * np.clip(2.6 - yy, 0, 1)
|
|
35
|
+
# Языки с резкими краями (степень для контраста)
|
|
36
|
+
tongues_raw = np.exp(-((xx_t * 5.0 + warp * 1.6) ** 2)) * np.clip(3.0 - yy, 0, 1)
|
|
37
|
+
tongues = np.clip(tongues_raw * 1.5 - 0.2, 0, 1) ** 0.7
|
|
38
|
+
lobe_raw = np.exp(-((xx_t * 6.0) ** 2)) * np.clip(3.2 - yy, 0, 1) * (0.6 + 0.4 * np.sin(t * 0.7 + yy * 4.0))
|
|
39
|
+
lobe = np.clip(lobe_raw * 1.4 - 0.15, 0, 1) ** 0.8
|
|
40
|
+
jets_raw = np.clip(turbulence - 0.15, 0, 1) * np.exp(-((xx_t * 7.0 + warp * 1.8) ** 2)) * np.clip(3.4 - yy, 0, 1)
|
|
41
|
+
jets = np.clip(jets_raw * 1.6 - 0.1, 0, 1) ** 0.6
|
|
42
|
+
rips = np.clip(turbulence * 1.4 - 0.35, 0, 1) * np.exp(-((xx_t * 4.2) ** 2)) * np.clip(2.2 - yy, 0, 1)
|
|
43
|
+
|
|
44
|
+
# Усиленные языки у основания (резкие)
|
|
45
|
+
base_tongues_raw = np.exp(-((xx_t * 3.5 + warp * 2.0) ** 2)) * np.clip(1.0 - yy * 0.6, 0, 1)
|
|
46
|
+
base_tongues = np.clip(base_tongues_raw * 1.5 - 0.15, 0, 1) ** 0.7
|
|
47
|
+
base_flicker = (0.7 + 0.3 * np.sin(t * 2.5 + xx * 8.0)) * np.clip(0.8 - yy, 0, 1)
|
|
48
|
+
base_texture = np.clip(detail * 0.8 + micro * 0.4, 0, 1) * np.exp(-((xx_t * 2.0) ** 2)) * np.clip(0.6 - yy * 0.5, 0, 1)
|
|
49
|
+
|
|
50
|
+
# Языки пламени веером от центра основания к краям
|
|
51
|
+
yy_safe = np.maximum(yy, 0.05) # избегаем деления на 0
|
|
52
|
+
# Больше языков под меньшими углами (более вертикальные)
|
|
53
|
+
tongue_angles = [-0.2, -0.13, -0.07, 0.0, 0.07, 0.13, 0.2] # 7 языков, узкий веер
|
|
54
|
+
tongues_radial = np.zeros_like(xx)
|
|
55
|
+
for i, ta in enumerate(tongue_angles):
|
|
56
|
+
# Ось языка с динамическим колебанием
|
|
57
|
+
wobble = noise.noise_3d(np.full_like(xx, i * 0.7), yy * 1.5 - t * 0.6, zz) * 0.08
|
|
58
|
+
tongue_axis = yy_safe * (ta + wobble)
|
|
59
|
+
dist_from_axis = np.abs(xx_t - tongue_axis)
|
|
60
|
+
# Ширина: больше у основания, сужается вверх
|
|
61
|
+
width = 0.10 * np.clip(1.0 - yy * 0.35, 0.25, 1.0)
|
|
62
|
+
tongue_shape = np.exp(-((dist_from_axis / width) ** 2))
|
|
63
|
+
# Контрастность: резкие края
|
|
64
|
+
tongue_shape = np.clip(tongue_shape * 1.5 - 0.2, 0, 1) ** 0.7
|
|
65
|
+
# Яркость: сильнее у основания, центральный язык ярче
|
|
66
|
+
center_boost = 1.0 + 0.4 * np.exp(-(ta * 8) ** 2) # центр ярче
|
|
67
|
+
brightness = np.clip(2.0 - yy * 1.0, 0, 1) * (0.8 + 0.2 * np.sin(t * 2.0 + i * 1.0)) * center_boost
|
|
68
|
+
tongues_radial += tongue_shape * brightness
|
|
69
|
+
tongues_radial = np.clip(tongues_radial * 0.6, 0, 1)
|
|
70
|
+
|
|
71
|
+
flame = np.clip(core * (0.2 + 1.0 * turbulence) + tongues * 1.0 + lobe * 0.7 + jets * 0.7 + rips * 0.4 + base_tongues * 0.8 + base_flicker * 0.4 + base_texture * 0.5 + tongues_radial * 0.7, 0, 1)
|
|
72
|
+
flame = np.clip((flame - 0.1) * 1.6, 0, 1)
|
|
73
|
+
|
|
74
|
+
# Подсветка ядра
|
|
75
|
+
core_glow = np.exp(-((xx_t * 3.2) ** 2 + (yy * 1.6) ** 2)) * np.clip(1.6 - yy, 0, 1)
|
|
76
|
+
|
|
77
|
+
# Дым: только в верхней части (выше yy=1.0) - полностью чистый низ
|
|
78
|
+
smoke_vertical = np.clip((yy - 1.0) * 2.0, 0, 1) # маска: 0 внизу, 1 вверху
|
|
79
|
+
smoke = np.clip((yy - 1.5) * 0.7, 0, 1) * (0.2 + 0.5 * turbulence)
|
|
80
|
+
side_mask = np.clip((np.abs(xx) * 1.8 - 0.5), 0, 1)
|
|
81
|
+
smoke = smoke * np.exp(-((xx_t * 1.5 + warp * 0.6) ** 2)) * side_mask
|
|
82
|
+
trail = np.exp(-((xx_t * 0.8 + warp * 0.6) ** 2)) * np.clip(yy - 1.2, 0, 1)
|
|
83
|
+
trail = trail * (0.3 + 0.5 * turbulence) * (0.5 + 0.5 * np.sin(t * 0.3 + yy * 1.5))
|
|
84
|
+
smoke = np.clip((smoke + trail * 0.4) * smoke_vertical, 0, 1)
|
|
85
|
+
|
|
86
|
+
# Искры: у основания без маски, выше - только по краям
|
|
87
|
+
spark_mask = np.clip(micro - 0.7, 0, 1)
|
|
88
|
+
edge = np.clip(np.abs(xx_t) * 2.0, 0, 1)
|
|
89
|
+
bottom_zone = np.clip(1.0 - yy, 0, 1)
|
|
90
|
+
sparks = spark_mask ** 2 * (bottom_zone * 1.2 + (1 - bottom_zone) * edge * 0.5)
|
|
91
|
+
|
|
92
|
+
rgb = np.full((h, w, 3), np.array([0.55, 0.27, 0.20], dtype=np.float32), dtype=np.float32)
|
|
93
|
+
heat = np.clip(flame * 1.2, 0, 1)
|
|
94
|
+
|
|
95
|
+
# Температурный градиент: краснее/оранжевее у основания, белее к ядру
|
|
96
|
+
vertical = np.clip(1.2 - yy, 0, 1)
|
|
97
|
+
temp = np.clip(vertical * 0.6 + heat * 0.7, 0, 1)
|
|
98
|
+
cold_color = np.array([1.0, 0.35, 0.02], dtype=np.float32) # более насыщенный оранжево-красный
|
|
99
|
+
hot_color = np.array([1.0, 0.92, 0.8], dtype=np.float32)
|
|
100
|
+
temp_rgb = cold_color * (1 - temp)[..., None] + hot_color * temp[..., None]
|
|
101
|
+
base_boost = np.clip(1.0 - yy * 0.3, 0.75, 1.0)
|
|
102
|
+
base_heat = np.clip(1.0 - yy * 0.5, 0, 1) * np.exp(-((xx_t * 1.8) ** 2))
|
|
103
|
+
# Дополнительная оранжевая текстура у основания (усилена)
|
|
104
|
+
orange_base = np.clip(detail * 0.5 + 0.5, 0, 1) * np.clip(0.8 - yy * 0.45, 0, 1) * np.exp(-((xx_t * 1.5) ** 2))
|
|
105
|
+
# Красное свечение у самого основания
|
|
106
|
+
red_base = np.clip(0.5 - yy * 0.6, 0, 1) * np.exp(-((xx_t * 1.2) ** 2)) * (0.7 + 0.3 * turbulence)
|
|
107
|
+
|
|
108
|
+
core_boost = 1.0 + 0.5 * core_glow
|
|
109
|
+
core_ripple = 0.85 + 0.15 * np.sin(t * 1.4 + (xx_t + yy) * 3.5) * core_glow
|
|
110
|
+
rgb[..., 0] = np.clip(temp_rgb[..., 0] * heat * core_boost * core_ripple * (1.15 * base_boost) + base_heat * 0.55 + orange_base * 0.7 + red_base * 0.5 + sparks * 1.6 + core_glow * 0.35, 0, 1)
|
|
111
|
+
rgb[..., 1] = np.clip(temp_rgb[..., 1] * heat * core_boost * core_ripple * (1.08 * base_boost) + base_heat * 0.35 + orange_base * 0.35 + red_base * 0.15 + sparks * 0.8 + core_glow * 0.2, 0, 1)
|
|
112
|
+
rgb[..., 2] = np.clip(temp_rgb[..., 2] * heat * (0.9 + 0.3 * core_glow) * core_ripple * (0.95 * base_boost) + base_heat * 0.03 + core_glow * 0.06, 0, 1)
|
|
113
|
+
|
|
114
|
+
# Добавляем дым как серо-синий слой
|
|
115
|
+
rgb[..., 0] = np.clip(rgb[..., 0] * (1 - smoke) + smoke * 0.3, 0, 1)
|
|
116
|
+
rgb[..., 1] = np.clip(rgb[..., 1] * (1 - smoke) + smoke * 0.35, 0, 1)
|
|
117
|
+
rgb[..., 2] = np.clip(rgb[..., 2] * (1 - smoke) + smoke * 0.4, 0, 1)
|
|
118
|
+
return rgb
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def main():
|
|
122
|
+
parser = argparse.ArgumentParser(description="Fire flame example")
|
|
123
|
+
add_interactive_args(parser)
|
|
124
|
+
args = parser.parse_args()
|
|
125
|
+
|
|
126
|
+
noise = SimplexNoise(seed=42)
|
|
127
|
+
|
|
128
|
+
if args.interactive:
|
|
129
|
+
config = InteractiveConfig.from_args(args, title="fractex")
|
|
130
|
+
|
|
131
|
+
def render_frame(t, w, h):
|
|
132
|
+
return _render_flame(t, w, h, noise)
|
|
133
|
+
|
|
134
|
+
run_interactive(render_frame, config)
|
|
135
|
+
else:
|
|
136
|
+
image = _render_flame(0.0, 512, 512, noise)
|
|
137
|
+
save_ppm(image, EXAMPLES_DIR / "output" / "fire_flame.ppm", stretch=True)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
if __name__ == "__main__":
|
|
141
|
+
main()
|
|
@@ -80,6 +80,7 @@ def main():
|
|
|
80
80
|
ema_ms = target_ms
|
|
81
81
|
phases = {name: 0.0 for name in presets}
|
|
82
82
|
last_t_base = 0.0
|
|
83
|
+
phase_wrap = 90.0
|
|
83
84
|
|
|
84
85
|
def _upscale_nearest(image: np.ndarray, out_w: int, out_h: int) -> np.ndarray:
|
|
85
86
|
in_h, in_w, _ = image.shape
|
|
@@ -103,20 +104,47 @@ def main():
|
|
|
103
104
|
base_name = presets[(frame // 240) % len(presets)]
|
|
104
105
|
detail_name = presets[(frame // 240 + 1) % len(presets)]
|
|
105
106
|
|
|
106
|
-
phases[base_name]
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
107
|
+
phases[base_name] = (phases[base_name] + delta_t) % phase_wrap
|
|
108
|
+
phases[detail_name] = (phases[detail_name] + delta_t) % phase_wrap
|
|
109
|
+
def _render_layer(base_preset: str, detail_preset: str) -> np.ndarray:
|
|
110
|
+
base_texture = textures[base_preset]
|
|
111
|
+
detail_texture = textures[detail_preset]
|
|
112
|
+
t = phases[base_preset]
|
|
113
|
+
t2 = phases[detail_preset]
|
|
114
|
+
|
|
115
|
+
base_zoom = 1.0 + 0.15 * np.sin(t * 0.25)
|
|
116
|
+
detail_zoom = 2.4 + 0.25 * np.sin(t2 * 0.35)
|
|
117
|
+
|
|
118
|
+
x0 = np.sin(t * 0.12) * 5.0
|
|
119
|
+
y0 = np.cos(t * 0.10) * 5.0
|
|
120
|
+
x1 = np.sin(t2 * 0.22 + 1.2) * 2.0
|
|
121
|
+
y1 = np.cos(t2 * 0.18 + 0.7) * 2.0
|
|
122
|
+
|
|
123
|
+
base_origin_x = -render_w / (2.0 * base_zoom)
|
|
124
|
+
base_origin_y = -render_h / (2.0 * base_zoom)
|
|
125
|
+
detail_origin_x = -render_w / (2.0 * detail_zoom)
|
|
126
|
+
detail_origin_y = -render_h / (2.0 * detail_zoom)
|
|
127
|
+
|
|
128
|
+
base = base_texture.generate_tile(
|
|
129
|
+
base_origin_x + x0,
|
|
130
|
+
base_origin_y + y0,
|
|
131
|
+
render_w,
|
|
132
|
+
render_h,
|
|
133
|
+
zoom=base_zoom,
|
|
134
|
+
)[..., :3]
|
|
135
|
+
detail = detail_texture.generate_tile(
|
|
136
|
+
detail_origin_x + x1,
|
|
137
|
+
detail_origin_y + y1,
|
|
138
|
+
render_w,
|
|
139
|
+
render_h,
|
|
140
|
+
zoom=detail_zoom,
|
|
141
|
+
)[..., :3]
|
|
142
|
+
|
|
143
|
+
depth = 0.35 + 0.15 * np.sin(t * 0.2)
|
|
144
|
+
rgb = np.clip(base * (1.0 - depth) + detail * depth, 0, 1)
|
|
145
|
+
tint = 0.6 + 0.4 * np.sin(t * 0.2)
|
|
146
|
+
rgb = np.clip(rgb * np.array([1.0, tint, 0.9]), 0, 1)
|
|
147
|
+
return rgb
|
|
120
148
|
|
|
121
149
|
now = time.perf_counter()
|
|
122
150
|
dt_ms = (now - last_time) * 1000.0
|
|
@@ -134,31 +162,20 @@ def main():
|
|
|
134
162
|
render_w = max(64, int(width * render_scale))
|
|
135
163
|
render_h = max(64, int(height * render_scale))
|
|
136
164
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
detail_origin_y + y1,
|
|
152
|
-
render_w,
|
|
153
|
-
render_h,
|
|
154
|
-
zoom=detail_zoom,
|
|
155
|
-
)[..., :3]
|
|
156
|
-
|
|
157
|
-
depth = 0.35 + 0.15 * np.sin(t * 0.2)
|
|
158
|
-
rgb_frame = np.clip(base * (1.0 - depth) + detail * depth, 0, 1)
|
|
159
|
-
|
|
160
|
-
tint = 0.6 + 0.4 * np.sin(t * 0.2)
|
|
161
|
-
rgb_frame = np.clip(rgb_frame * np.array([1.0, tint, 0.9]), 0, 1)
|
|
165
|
+
rgb_frame = _render_layer(base_name, detail_name)
|
|
166
|
+
if not selected:
|
|
167
|
+
cycle_len = 240
|
|
168
|
+
transition_len = 60
|
|
169
|
+
cycle_pos = frame % cycle_len
|
|
170
|
+
if cycle_pos >= cycle_len - transition_len:
|
|
171
|
+
next_base = presets[(frame // cycle_len + 1) % len(presets)]
|
|
172
|
+
next_detail = presets[(frame // cycle_len + 2) % len(presets)]
|
|
173
|
+
phases[next_base] = (phases[next_base] + delta_t) % phase_wrap
|
|
174
|
+
phases[next_detail] = (phases[next_detail] + delta_t) % phase_wrap
|
|
175
|
+
next_rgb = _render_layer(next_base, next_detail)
|
|
176
|
+
w = (cycle_pos - (cycle_len - transition_len)) / transition_len
|
|
177
|
+
w = w * w * (3.0 - 2.0 * w)
|
|
178
|
+
rgb_frame = np.clip(rgb_frame * (1.0 - w) + next_rgb * w, 0, 1)
|
|
162
179
|
rgb_frame = _upscale_nearest(rgb_frame, width, height)
|
|
163
180
|
im.set_array(rgb_frame)
|
|
164
181
|
return (im,)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fractex
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Fractal texture engine for procedural and infinite-detail textures.
|
|
5
5
|
Author-email: Timur Isanov <tisanov@yahoo.com>
|
|
6
6
|
License: MIT License
|
|
@@ -43,6 +43,7 @@ Description-Content-Type: text/markdown
|
|
|
43
43
|
License-File: LICENSE
|
|
44
44
|
Requires-Dist: numpy
|
|
45
45
|
Requires-Dist: numba
|
|
46
|
+
Requires-Dist: matplotlib
|
|
46
47
|
Dynamic: license-file
|
|
47
48
|
|
|
48
49
|
## Fractex
|
|
@@ -30,6 +30,7 @@ fractex/examples/atmosphere.py
|
|
|
30
30
|
fractex/examples/composite_material.py
|
|
31
31
|
fractex/examples/crystal_cave.py
|
|
32
32
|
fractex/examples/custom_pattern.py
|
|
33
|
+
fractex/examples/fire_flame.py
|
|
33
34
|
fractex/examples/game_integration.py
|
|
34
35
|
fractex/examples/game_texture.py
|
|
35
36
|
fractex/examples/integration.py
|
|
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "fractex"
|
|
7
|
-
version = "0.1
|
|
7
|
+
version = "0.2.1"
|
|
8
8
|
description = "Fractal texture engine for procedural and infinite-detail textures."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
11
11
|
license = { file = "LICENSE" }
|
|
12
12
|
authors = [{ name = "Timur Isanov", email = "tisanov@yahoo.com" }]
|
|
13
|
-
dependencies = ["numpy", "numba"]
|
|
13
|
+
dependencies = ["numpy", "numba", "matplotlib"]
|
|
14
14
|
keywords = ["fractal", "texture", "procedural", "noise"]
|
|
15
15
|
classifiers = [
|
|
16
16
|
"Development Status :: 3 - Alpha",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|