DirectionalForce
Constraint that applies constant force in a specific direction
Constructor
new DirectionalForce(params: DirectionalForceParams)
Parameters
Parameter | Type | Description | Default |
---|---|---|---|
params | DirectionalForceParams | Configuration parameters for the force | - |
Properties
Property | Type | Description |
---|---|---|
name | string | Unique identifier for the constraint |
active | boolean | Whether the constraint is active |
glsl | string | GLSL shader code for the force |
strength | [number, number, number] | Force direction and magnitude |
Methods
build(): void
Builds the GLSL shader code for the directional force.
setStrength(strength: [number, number, number]): void
Updates the strength and direction of the force.
Example Usage
import { DirectionalForce, PropertyManager } from 'particlize';
// Create gravity force
const gravity = new DirectionalForce({
strength: { value: [0, -9.81, 0], hardcode: false }
});
// Create wind force
const wind = new DirectionalForce({
strength: { value: [2.0, 0, 1.0], hardcode: true }
});
// Add to property manager
const propertyManager = new PropertyManager({ /* ... */ });
propertyManager.addProperty('velocity', { size: 3 });
propertyManager.addConstraint('velocity', gravity);
// Update force dynamically
gravity.setStrength([0, -5.0, 0]);
import { DirectionalForce } from 'particlize';
// Gravity force
const gravity = new DirectionalForce('gravity', {
strength: { value: [0, -9.81, 0], hardcode: true }
});
// Wind force
const wind = new DirectionalForce('wind', {
strength: { value: [2.0, 0, 0.5], hardcode: false }
});
// Upward acceleration
const lift = new DirectionalForce('lift', {
strength: { value: [0, 5.0, 0], hardcode: false }
});
Properties
Default Parameters
static readonly defaultParams: DirectionalForceParams = {
strength: { value: [0, 0, 0], hardcode: false }
};
Instance Properties
name
:string
- Constraint nameparams
:DirectionalForceParams
- Force parametersglsl
:string
- Generated GLSL codeuniforms
:Record<string, any>
- Shader uniforms (for non-hardcoded params)
GLSL Implementation
The DirectionalForce generates the following GLSL code:
force = STRENGTH * mass;
Where:
STRENGTH
is replaced with the strength parameter value or uniform- The force is applied uniformly to all particles
- Mass scaling ensures proper physics behavior
Usage Examples
Basic Gravity
import { DirectionalForce, PropertyManager } from 'particlize';
// Earth-like gravity
const gravity = new DirectionalForce('gravity', {
strength: { value: [0, -9.81, 0], hardcode: true }
});
// Apply to force property
propertyManager.constrain('force', gravity);
Wind Effects
// Gentle breeze
const breeze = new DirectionalForce('breeze', {
strength: { value: [1.0, 0, 0], hardcode: false }
});
// Strong wind with vertical component
const strongWind = new DirectionalForce('strongWind', {
strength: { value: [5.0, 1.0, 2.0], hardcode: false }
});
propertyManager
.constrain('force', breeze)
.constrain('force', strongWind); // Forces combine additively
Dynamic Force Direction
// Use uniforms for dynamic force direction
const dynamicForce = new DirectionalForce('dynamic', {
strength: { value: [0, 0, 0], hardcode: false }
});
propertyManager.constrain('force', dynamicForce);
// Update force direction in animation loop
function updateForceDirection(direction: THREE.Vector3, magnitude: number) {
const force = direction.normalize().multiplyScalar(magnitude);
propertyManager.setUniforms('force', {
u_dynamic_strength: force.toArray()
});
}
// Example: Force follows mouse
updateForceDirection(
new THREE.Vector3(mouseX, mouseY, 0).sub(centerPoint),
2.0
);
Multiple Directional Forces
// Gravity
const gravity = new DirectionalForce('gravity', {
strength: { value: [0, -9.81, 0], hardcode: true }
});
// Wind
const wind = new DirectionalForce('wind', {
strength: { value: [2.0, 0, 0], hardcode: false }
});
// Magnetic field
const magnetic = new DirectionalForce('magnetic', {
strength: { value: [0, 0, 1.0], hardcode: false }
});
propertyManager
.constrain('force', gravity)
.constrain('force', wind)
.constrain('force', magnetic);
Time-varying Forces
class TimeVaryingForce {
private force: DirectionalForce;
private propertyManager: PropertyManager;
private baseStrength: THREE.Vector3;
constructor(propertyManager: PropertyManager, baseStrength: THREE.Vector3) {
this.propertyManager = propertyManager;
this.baseStrength = baseStrength.clone();
this.force = new DirectionalForce('timeVarying', {
strength: { value: [0, 0, 0], hardcode: false }
});
propertyManager.constrain('force', this.force);
}
update(time: number) {
// Sinusoidal variation
const factor = Math.sin(time * 2.0) * 0.5 + 0.5; // 0 to 1
const currentStrength = this.baseStrength.clone().multiplyScalar(factor);
this.propertyManager.setUniforms('force', {
u_timeVarying_strength: currentStrength.toArray()
});
}
}
// Usage
const varyingForce = new TimeVaryingForce(
propertyManager,
new THREE.Vector3(0, -10, 0)
);
// In animation loop
varyingForce.update(performance.now() * 0.001);
Environmental Forces
// Simulate underwater environment
const buoyancy = new DirectionalForce('buoyancy', {
strength: { value: [0, 2.0, 0], hardcode: true } // Upward
});
const waterResistance = new DirectionalForce('resistance', {
strength: { value: [0, -0.5, 0], hardcode: true } // Slight downward
});
const current = new DirectionalForce('current', {
strength: { value: [1.0, 0, 0.5], hardcode: false } // Horizontal flow
});
propertyManager
.constrain('force', buoyancy)
.constrain('force', waterResistance)
.constrain('force', current);
Interactive Directional Forces
class InteractiveForceField {
private forces: DirectionalForce[] = [];
private propertyManager: PropertyManager;
constructor(propertyManager: PropertyManager) {
this.propertyManager = propertyManager;
this.setupForces();
}
private setupForces() {
// Create multiple directional forces for different zones
const zones = [
{ name: 'zone1', direction: [1, 0, 0] },
{ name: 'zone2', direction: [0, 1, 0] },
{ name: 'zone3', direction: [-1, 0, 0] },
{ name: 'zone4', direction: [0, -1, 0] }
];
zones.forEach(zone => {
const force = new DirectionalForce(zone.name, {
strength: { value: [0, 0, 0], hardcode: false }
});
this.forces.push(force);
this.propertyManager.constrain('force', force);
});
}
activateZone(zoneIndex: number, strength: number) {
this.forces.forEach((force, index) => {
if (index === zoneIndex) {
const direction = [
[1, 0, 0], [0, 1, 0], [-1, 0, 0], [0, -1, 0]
][index];
const forceVector = direction.map(d => d * strength);
this.propertyManager.setUniforms('force', {
[`u_${force.name}_strength`]: forceVector
});
} else {
// Deactivate other zones
this.propertyManager.setUniforms('force', {
[`u_${force.name}_strength`]: [0, 0, 0]
});
}
});
}
}
Parameter Configuration
Force Strength
// Weak force (subtle effect)
new DirectionalForce('weak', {
strength: { value: [0, -0.1, 0], hardcode: true }
});
// Medium force (noticeable effect)
new DirectionalForce('medium', {
strength: { value: [0, -2.0, 0], hardcode: false }
});
// Strong force (dramatic effect)
new DirectionalForce('strong', {
strength: { value: [0, -10.0, 0], hardcode: true }
});
// Multi-directional force
new DirectionalForce('multidirectional', {
strength: { value: [1.5, -3.0, 0.8], hardcode: false }
});
Hardcoded vs Uniform
// Static force (better performance)
const staticGravity = new DirectionalForce('staticGravity', {
strength: { value: [0, -9.81, 0], hardcode: true }
});
// Dynamic force (updateable at runtime)
const dynamicWind = new DirectionalForce('dynamicWind', {
strength: { value: [2.0, 0, 0], hardcode: false }
});
Integration with ParticleSystem
import { ParticleSystem, DirectionalForce, SamplerFrame } from 'particlize';
const particleSystem = new ParticleSystem({ canvas });
// Setup properties
particleSystem.manager
.add('position', 3)
.add('velocity', 3, new Float32Array([0, 0, 0]))
.add('force', 3, new Float32Array([0, 0, 0]))
.add('mass', 1, new Float32Array([1.0]));
// Add gravity
const gravity = new DirectionalForce('gravity', {
strength: { value: [0, -9.81, 0], hardcode: true }
});
// Add wind
const wind = new DirectionalForce('wind', {
strength: { value: [1.0, 0, 0], hardcode: false }
});
particleSystem.manager
.constrain('force', gravity)
.constrain('force', wind);
// Add particles
const frame = new SamplerFrame({ sampler, count: 5000 });
particleSystem.addParticles(frame);
Force Combination
Multiple DirectionalForce constraints are additive:
// Force 1: [1, 0, 0]
// Force 2: [0, -2, 0]
// Force 3: [0.5, 0, 1]
// Total force: [1.5, -2, 1]
This allows for complex force fields by combining simple directional components.
Performance Considerations
- Hardcoded Forces: Better performance for static forces
- Uniform Updates: Slight overhead for dynamic forces
- Force Count: Each force adds minimal computational cost
- Memory Usage: Uniforms consume GPU memory
Common Use Cases
- Gravity: Downward acceleration for realistic physics
- Wind: Horizontal forces for atmospheric effects
- Magnetic Fields: Directional pull/push forces
- Conveyor Belts: Constant transport forces
- Acceleration Zones: Speed boosts in specific directions
- Environmental Effects: Rain, snow, underwater currents
- Game Mechanics: Jump pads, wind tunnels, force fields
Best Practices
- Force Scaling: Consider particle mass in force calculations
- Realistic Values: Use physically plausible force magnitudes
- Performance: Use hardcoded parameters for static forces
- Combination: Layer multiple forces for complex effects
- Debugging: Start with simple forces and add complexity gradually
- Mass Independence: Consider whether force should depend on mass
Troubleshooting
Common Issues
// Issue: No visible effect
// Solution: Check force magnitude and mass values
console.log('Force strength:', force.params.strength.value);
console.log('Particle mass:', /* check mass property */);
// Issue: Forces too strong
// Solution: Reduce strength values
force.params.strength.value = [0, -0.1, 0]; // Weaker gravity
// Issue: Inconsistent behavior
// Solution: Ensure mass property exists and has reasonable values
propertyManager.add('mass', 1, new Float32Array([1.0]));
Debugging
// Log all active forces
const forceProperty = propertyManager.properties.get('force');
if (forceProperty) {
console.log('Active constraints:', forceProperty.fbo.constraints.keys());
}
// Check uniform values
console.log('Wind strength uniform:', propertyManager.getUniforms('force'));
Advanced Usage
Conditional Forces
// Force that only applies under certain conditions
class ConditionalDirectionalForce extends Constraint {
constructor(name: string, direction: [number, number, number], strength: number) {
super(name, `
// Only apply force if particle is above ground
if (position.y > 0.0) {
force += vec3(${direction.join(', ')}) * ${strength} * mass;
}
`);
}
}
const conditionalGravity = new ConditionalDirectionalForce(
'conditionalGravity',
[0, -9.81, 0],
1.0
);
Distance-based Forces
// Directional force that weakens with distance from origin
class DistanceBasedDirectionalForce extends Constraint {
constructor(name: string, direction: [number, number, number], strength: number) {
super(name, `
float distance = length(position);
float falloff = 1.0 / (1.0 + distance * 0.1);
vec3 forceDirection = vec3(${direction.join(', ')});
force += forceDirection * ${strength} * falloff * mass;
`);
}
}
Turbulent Forces
// Directional force with noise-based variation
class TurbulentDirectionalForce extends Constraint {
constructor(name: string, baseDirection: [number, number, number], strength: number) {
super(name, `
// Add noise to base direction
vec3 noiseOffset = vec3(
sin(position.x * 0.1 + u_time),
cos(position.y * 0.1 + u_time),
sin(position.z * 0.1 + u_time)
) * 0.3;
vec3 turbulentDirection = vec3(${baseDirection.join(', ')}) + noiseOffset;
force += turbulentDirection * ${strength} * mass;
`);
}
}
The DirectionalForce constraint provides a simple yet powerful way to create uniform force fields that affect all particles consistently, forming the foundation for many physics-based particle behaviors.