particri / examples /10_trapcode_particular_features.py
ArifiIslam's picture
Add Trapcode Particular VFX systems: Aux trails, Layer emitter, Bounce physics, Curves over life (v1.1.0)
e14f09d verified
Raw History Blame Contribute Delete
5.18 kB
"""
Example 10: Trapcode Particular Iconic Features Showcase
Demonstrates:
1. Auxiliary Particle System (Aux Trails & Secondary Sparkle Dust)
2. Floor Collision & Bounce Physics (Elasticity & Surface Friction)
3. Custom Curves Over Life (Bell, GrowAndFade, Pulse)
4. Layer Mask Emitter (Particle Birth from Image/Logo with Pixel Color Inheritance)
"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
import numpy as np
from PIL import Image, ImageDraw, ImageFont
import particri as pc
output_dir = Path("output")
output_dir.mkdir(exist_ok=True)
print("--- 1. Generating Aux Trails & Floor Bounce Demo ---")
engine_bounce = pc.ParticleEngine(max_particles=35000, width=1920, height=1080)
# Add Floor Bounce Plane at y=920
engine_bounce.add_bounce_plane(coord=920.0, bounce=0.62, friction=0.88, is_horizontal=True, is_max=True)
# Add Gravity & subtle atmospheric drag
engine_bounce.add_gravity(0.0, 180.0)
engine_bounce.add_drag(0.08)
# Add slight curl noise turbulence
engine_bounce.add_curl_noise(seed=42, strength=60.0, frequency=0.002, speed=0.2)
# Primary Emitter: Golden Fountain shooting upwards & outwards
engine_bounce.add_emitter(
shape=pc.EmitterShape.Circle,
position=(960.0, 750.0),
size=(30.0, 30.0),
rate=180.0,
speed_range=(250.0, 520.0),
angle_range=(-2.35, -0.78), # Upward fan
lifetime_range=(1.8, 3.2),
size_start_range=(8.0, 18.0),
size_end_range=(1.0, 3.0),
color_start=(1.0, 0.9, 0.5, 1.0),
color_end=(1.0, 0.35, 0.05, 0.0),
particle_shape=pc.ParticleShape.Star4,
size_curve=pc.CurveType.GrowAndFade,
opacity_curve=pc.CurveType.GrowAndFade,
seed=101,
)
# Auxiliary System: Each falling firework spark leaves shimmering golden dust trails!
engine_bounce.enable_aux_system(
rate=25.0, # 25 child particles per second from each primary particle
lifetime=1.4,
size_range=(4.0, 0.5),
color_start=(1.0, 0.85, 0.3, 0.95),
color_end=(1.0, 0.2, 0.05, 0.0),
inherit_color=True,
velocity_inheritance=0.12, # Child inherits 12% parent speed
spread=18.0, # Slight jitter
particle_shape=pc.ParticleShape.Glitter,
size_curve=pc.CurveType.Bell, # Peaks in middle, gentle fade
opacity_curve=pc.CurveType.GrowAndFade,
emit_at_death=8, # 8 sparks burst on particle death
)
# Simulate 3.5 seconds
print("Simulating physics & particle lifecycles...")
engine_bounce.warmup(duration=3.5, step=1.0 / 60.0)
frame_bounce = engine_bounce.render(blend_mode=pc.BlendMode.Additive, bloom=True, bloom_intensity=0.85, bloom_radius=8)
pc.Exporter.save_image(frame_bounce, output_dir / "10_particular_aux_trails_bounce.png")
print(f"Saved: {output_dir / '10_particular_aux_trails_bounce.png'}")
print("\n--- 2. Generating Layer Mask / Logo Emitter Demo ---")
engine_layer = pc.ParticleEngine(max_particles=30000, width=1920, height=1080)
# Create a vibrant synthetic logo image mask (1920x1080) with PIL
logo_img = Image.new("RGBA", (1920, 1080), (0, 0, 0, 0))
draw = ImageDraw.Draw(logo_img)
# Draw central geometric glowing star emblem with gradient rings
cx, cy = 960, 540
# Outer ring (Cyan / Teal)
draw.ellipse([cx - 280, cy - 280, cx + 280, cy + 280], outline=(0, 220, 255, 255), width=8)
# Middle ring (Magenta / Pink)
draw.ellipse([cx - 200, cy - 200, cx + 200, cy + 200], outline=(255, 60, 180, 255), width=6)
# Inner ring (Warm Gold)
draw.ellipse([cx - 120, cy - 120, cx + 120, cy + 120], outline=(255, 215, 80, 255), width=5)
# Central diamond star polygon
def make_star(center_x, center_y, r_outer, r_inner, num_pts=8):
pts = []
for i in range(num_pts * 2):
r = r_outer if i % 2 == 0 else r_inner
angle = i * np.pi / num_pts - np.pi / 2
pts.append((center_x + r * np.cos(angle), center_y + r * np.sin(angle)))
return pts
star_pts = make_star(cx, cy, 100, 35, num_pts=8)
draw.polygon(star_pts, fill=(255, 240, 200, 255), outline=(255, 180, 50, 255))
# Add Layer Emitter to engine with automatic pixel color inheritance!
mask_np = np.asarray(logo_img, dtype=np.float32) / 255.0
engine_layer.add_layer_emitter(
image=mask_np,
rate=2800.0,
speed_range=(8.0, 45.0),
lifetime_range=(1.2, 3.0),
size_start_range=(5.0, 14.0),
size_end_range=(0.0, 2.0),
particle_shape=pc.ParticleShape.Star8,
size_curve=pc.CurveType.Bell,
opacity_curve=pc.CurveType.GrowAndFade,
inherit_layer_color=True,
drag=0.18,
seed=777,
)
# Add subtle vortex swirl around the logo
engine_layer.add_vortex(center=(cx, cy), strength=70.0, radial_pull=-10.0, radius=600.0)
engine_layer.add_curl_noise(seed=999, strength=50.0, frequency=0.003, speed=0.2)
print("Simulating logo layer emission...")
engine_layer.warmup(duration=2.5, step=1.0 / 60.0)
frame_layer = engine_layer.render(blend_mode=pc.BlendMode.Screen, bloom=True, bloom_intensity=0.9, bloom_radius=7)
pc.Exporter.save_image(frame_layer, output_dir / "10_particular_layer_mask_logo.png")
print(f"Saved: {output_dir / '10_particular_layer_mask_logo.png'}")
print("\nAll Trapcode Particular demos generated successfully!")