Skip to content

Commit

Permalink
chore: test pipeline and web workers
Browse files Browse the repository at this point in the history
  • Loading branch information
Gugustinette committed Dec 27, 2024
1 parent f336bb9 commit 38f689a
Show file tree
Hide file tree
Showing 19 changed files with 349 additions and 165 deletions.
1 change: 1 addition & 0 deletions packages/3d/build.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { defineBuildConfig } from 'unbuild'
export default defineBuildConfig({
entries: [
'src/index',
'src/pipeline/PhysicWorkerRun',
],
declaration: true,
clean: true,
Expand Down
32 changes: 6 additions & 26 deletions packages/3d/src/core/FScene.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import type RAPIER from '@dimforge/rapier3d'
import type { FCamera } from '../cameras/FCamera'
import { FFixedCamera } from '../cameras/FFixedCamera'
import type { FLight } from '../lights/FLight'
import { RenderPipeline } from '../pipeline/RenderPipeline'
import { PhysicPipeline } from '../pipeline/PhysicPipeline'
import type { FComponent } from './FComponent'
import type { FRigidBody } from './FRigidBody'
import type { FCollider } from './FCollider'
Expand Down Expand Up @@ -115,17 +117,8 @@ export class FScene extends FSceneCore {
// Add renderer to DOM
this.__DOM_NODE__.appendChild(this.renderer.domElement)

// Each frame
this.onFrame((delta) => {
// Call frame for each component
this.components.forEach(component => component.frame(delta))

// Call frame for the camera
this.camera.frame(delta)

// Render the scene
this.renderer.render(this.scene, this.camera.__CAMERA__)
})
// Initialize the render pipeline
this.addStandardPipeline(new RenderPipeline({ scene: this }))

// Call the onReady callbacks
this.__CALLBACKS_ON_READY__.forEach((callback) => {
Expand All @@ -143,21 +136,8 @@ export class FScene extends FSceneCore {
// Initialize Rapier event queue
this.eventQueue = new RAPIER.EventQueue(true)

// onFrame loop
this.onFrame((delta) => {
// Step the physics world
this.world.timestep = delta
this.world.step(this.eventQueue)

// Call frame for each collider and rigidBody
this.colliders.forEach(collider => collider.frame(delta))
this.rigidBodies.forEach(rigidBody => rigidBody.frame(delta))

// Drain collision events
this.eventQueue.drainCollisionEvents((handle1: RAPIER.ColliderHandle, handle2: RAPIER.ColliderHandle, started: boolean) => {
this.handleCollision(handle1, handle2, started)
})
})
// Initialize the physic pipeline
this.addStandardPipeline(new PhysicPipeline({ scene: this }))
}

/**
Expand Down
35 changes: 35 additions & 0 deletions packages/3d/src/pipeline/PhysicPipeline.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { StandardPipeline } from '@fibbojs/core'
import type RAPIER from '@dimforge/rapier3d'
import type { FScene } from '../core/FScene'

export interface PhysicPipelineOptions {
scene: FScene
}

/**
* Render pipeline.
*/
export class PhysicPipeline extends StandardPipeline {
scene: FScene

constructor(options: PhysicPipelineOptions) {
super()
this.scene = options.scene
this.frameRate = 60
}

frame(delta: number) {
// Step the physics world
this.scene.world.timestep = delta
this.scene.world.step(this.scene.eventQueue)

// Call frame for each collider and rigidBody
this.scene.colliders.forEach(collider => collider.frame(delta))
this.scene.rigidBodies.forEach(rigidBody => rigidBody.frame(delta))

// Drain collision events
this.scene.eventQueue.drainCollisionEvents((handle1: RAPIER.ColliderHandle, handle2: RAPIER.ColliderHandle, started: boolean) => {
this.scene.handleCollision(handle1, handle2, started)
})
}
}
20 changes: 20 additions & 0 deletions packages/3d/src/pipeline/PhysicPipelineTest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { BackgroundPipeline } from '@fibbojs/core'

export enum PhysicPipelineCommands {
START = 'start',
STOP = 'stop',
}

/**
* Physic pipeline.
* @category Pipeline
*/
export class PhysicPipelineTest extends BackgroundPipeline {
constructor() {
super('../../3d/dist/pipeline/PhysicWorkerRun.mjs')
}

frame(_delta: number) {
// console.log('PhysicPipeline frame')
}
}
30 changes: 30 additions & 0 deletions packages/3d/src/pipeline/PhysicWorker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { BackgroundWorker } from '@fibbojs/core'

/**
* Physic web worker.
* @category Pipeline
*/
export class PhysicWorker extends BackgroundWorker {
constructor(sw: DedicatedWorkerGlobalScope) {
super(sw)
}

frame(delta: number) {
console.log(`Worker : ${delta}`)

/*
// Step the physics world
this.world.timestep = delta
this.world.step(this.eventQueue)
// Call frame for each collider and rigidBody
this.colliders.forEach(collider => collider.frame(delta))
this.rigidBodies.forEach(rigidBody => rigidBody.frame(delta))
// Drain collision events
this.eventQueue.drainCollisionEvents((handle1: RAPIER.ColliderHandle, handle2: RAPIER.ColliderHandle, started: boolean) => {
this.handleCollision(handle1, handle2, started)
})
*/
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/// <reference lib="WebWorker" />

import { RenderPipeline } from './RenderPipeline'
import { PhysicWorker } from './PhysicWorker'

export type {}
declare let self: DedicatedWorkerGlobalScope

new RenderPipeline(self)
new PhysicWorker(self)
30 changes: 30 additions & 0 deletions packages/3d/src/pipeline/RenderPipeline.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { StandardPipeline } from '@fibbojs/core'
import type { FScene } from '../core/FScene'

export interface RenderPipelineOptions {
scene: FScene
}

/**
* Render pipeline.
*/
export class RenderPipeline extends StandardPipeline {
scene: FScene

constructor(options: RenderPipelineOptions) {
super()
this.scene = options.scene
this.frameRate = 60
}

frame(delta: number) {
// Call frame for each component
this.scene.components.forEach(component => component.frame(delta))

// Call frame for the camera
this.scene.camera.frame(delta)

// Render the scene
this.scene.renderer.render(this.scene.scene, this.scene.camera.__CAMERA__)
}
}
2 changes: 0 additions & 2 deletions packages/core/build.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import { defineBuildConfig } from 'unbuild'
export default defineBuildConfig({
entries: [
'src/index',
'src/pipeline/RenderPipelineWorker',
'src/pipeline/PhysicPipelineWorker',
],
declaration: true,
clean: true,
Expand Down
93 changes: 68 additions & 25 deletions packages/core/src/FScene.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import type RAPIER2D from '@dimforge/rapier2d'
import type RAPIER3D from '@dimforge/rapier3d'
import type { FComponent } from './FComponent'
import type { FLight } from './FLight'
import { CustomWorker } from './pipeline/CustomWorker'
import type { BackgroundPipeline } from './pipeline/BackgroundPipeline'
import type { StandardPipeline } from './pipeline/StandardPipeline'
import { MainPipeline } from './pipeline/MainPipeline'
import { PipelineState } from './pipeline/Pipeline'

export interface FSceneOptions {
gravity?: { x: number, y: number, z: number } | { x: number, y: number }
Expand All @@ -16,17 +19,15 @@ export interface FSceneOptions {
* @category Core
*/
export abstract class FScene {
/**
* Internal flags
*/
// Internal flags
public __IS_3D__: boolean = false
public __IS_2D__: boolean = false

/**
* Pipelines
*/
private __RENDER_PIPELINE__: CustomWorker | null = null
private __PHYSIC_PIPELINE__: CustomWorker | null = null
__STANDARD_PIPELINES__: StandardPipeline[]
__BACKGROUND_PIPELINES__: BackgroundPipeline[]

/**
* DOM element that the renderer will be appended to
Expand Down Expand Up @@ -97,40 +98,44 @@ export abstract class FScene {
this.__DOM_NODE__ = options.domNode

/**
* Time management
* Pipelines
*/
let lastTime = (new Date()).getTime()
let currentTime = 0
let delta = 0
this.__STANDARD_PIPELINES__ = []
this.__BACKGROUND_PIPELINES__ = []
this.addStandardPipeline(new MainPipeline({ scene: this }))

/**
* Auto loop function that calls the frame method every frame.
*/
const autoLoop = () => {
requestAnimationFrame(autoLoop)

// Calculate delta time
currentTime = (new Date()).getTime()
delta = (currentTime - lastTime) / 1000
lastTime = currentTime

// Call onFrame callbacks
this.frame(delta)
this.__STANDARD_PIPELINES__.forEach((pipeline) => {
// If the pipeline should be running
if (pipeline.state === PipelineState.RUNNING) {
// Calculate elapsed time
const currentTime = (new Date()).getTime()
const elapsedTime = currentTime - pipeline.lastTime
// If enough time has passed to match the expected framerate
if (elapsedTime > 1000 / pipeline.frameRate) {
const delta = (currentTime - pipeline.lastTime) / 1000
// Keep track of the last time the pipeline was called
pipeline.lastTime = currentTime
// Call the pipeline
pipeline.frame(delta)
}
}
})
}

if (options.autoLoop)
autoLoop()

// Initialize the components array
this.components = []
// Initialize the lights array
this.lights = []

// Initialize workers
this.__RENDER_PIPELINE__ = new CustomWorker('./pipeline/RenderPipelineWorker.mjs')
this.__RENDER_PIPELINE__.start()
this.__PHYSIC_PIPELINE__ = new CustomWorker('./pipeline/PhysicPipelineWorker.mjs')
this.__PHYSIC_PIPELINE__.start()
// Launch the autoLoop if needed
if (options.autoLoop)
autoLoop()
}

/**
Expand Down Expand Up @@ -171,6 +176,44 @@ export abstract class FScene {
this.__CALLBACKS_ON_LIGHT_REMOVED__.forEach(callback => callback(light))
}

/**
* Add a standard pipeline.
*/
addStandardPipeline(pipeline: StandardPipeline) {
this.__STANDARD_PIPELINES__.push(pipeline)
pipeline.start()
}

/**
* Remove a standard pipeline.
*/
removeStandardPipeline(pipeline: StandardPipeline) {
pipeline.stop()
const index = this.__STANDARD_PIPELINES__.indexOf(pipeline)
if (index !== -1) {
this.__STANDARD_PIPELINES__.splice(index, 1)
}
}

/**
* Add a background pipeline.
*/
addBackgroundPipeline(pipeline: BackgroundPipeline) {
this.__BACKGROUND_PIPELINES__.push(pipeline)
pipeline.start()
}

/**
* Remove a background pipeline.
*/
removeBackgroundPipeline(pipeline: BackgroundPipeline) {
pipeline.stop()
const index = this.__BACKGROUND_PIPELINES__.indexOf(pipeline)
if (index !== -1) {
this.__BACKGROUND_PIPELINES__.splice(index, 1)
}
}

/**
* Compute a frame with the given delta time.
* By default, it is called every frame, but this behavior can be changed by giving the `autoLoop` option as `false` when creating the scene.
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ export * from './FLight'
export * from './FScene'

// Pipeline
export * from './pipeline/CustomWorker'
export * from './pipeline/PhysicPipeline'
export * from './pipeline/BackgroundPipeline'
export * from './pipeline/BackgroundWorker'
export * from './pipeline/Pipeline'
export * from './pipeline/RenderPipeline'
export * from './pipeline/StandardPipeline'

// Types
export * from './types/FVector2'
Expand Down
33 changes: 33 additions & 0 deletions packages/core/src/pipeline/BackgroundPipeline.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Pipeline, PipelineCommands } from './Pipeline'

/**
* A pipeline that abstract the usage of a web worker.
* It provides better type checking, and more control over the worker.
* @category Pipeline
*/
export abstract class BackgroundPipeline extends Pipeline {
worker: Worker

constructor(path: string) {
super()
/*
console.log(import.meta.url)
console.log(new URL(path, import.meta.url).href)
*/
this.worker = new Worker(new URL(path, import.meta.url), { type: 'module' })
}

/**
* Start the corresponding pipeline.
*/
start(): void {
this.worker.postMessage(PipelineCommands.START)
}

/**
* Stop the corresponding pipeline.
*/
stop(): void {
this.worker.postMessage(PipelineCommands.STOP)
}
}
Loading

0 comments on commit 38f689a

Please sign in to comment.