diff --git a/apps/web/src/common/models/piece.model.ts b/apps/web/src/common/models/piece.model.ts index f24458a..68278ee 100644 --- a/apps/web/src/common/models/piece.model.ts +++ b/apps/web/src/common/models/piece.model.ts @@ -4,6 +4,7 @@ import { Subject } from "rxjs"; import type { BoardCoords, PieceId } from "../interfaces"; import { ColorVariant, PieceType } from "../enums"; import { MATRIX, QUATERNION, SCALE, VECTOR } from "../constants"; +import { PhysicsProperties } from "@chess-d/rapier-physics/dist/types"; export class PieceModel< T extends PieceType = PieceType, @@ -18,6 +19,7 @@ export class PieceModel< public readonly id: PieceId, public readonly type: T, public readonly color: C, + public readonly physics: PhysicsProperties, public readonly promotedFromType?: PieceType ) { super(); @@ -39,7 +41,7 @@ export class PieceModel< MATRIX.decompose(VECTOR, QUATERNION, SCALE); VECTOR.setY(VECTOR.y + yOffset); - this.setPosition(VECTOR); + this.physics.rigidBody.setTranslation(VECTOR, true); this.update$$.next(this); } } diff --git a/apps/web/src/common/models/pieces-group.model.ts b/apps/web/src/common/models/pieces-group.model.ts index b00234a..015f0f9 100644 --- a/apps/web/src/common/models/pieces-group.model.ts +++ b/apps/web/src/common/models/pieces-group.model.ts @@ -1,10 +1,12 @@ import { BufferGeometry, DynamicDrawUsage, InstancedMesh } from "three"; import { Subject, Subscription } from "rxjs"; +import { Physics } from "@chess-d/rapier-physics"; import { ColorVariant, PieceType } from "../enums"; import { PieceModel } from "./piece.model"; import { COLOR_BLACK, COLOR_WHITE } from "../constants"; import { BoardCoords, PieceId } from "../interfaces"; +import { PhysicsProperties } from "@chess-d/rapier-physics/dist/types"; export class PiecesGroupModel< type extends PieceType, @@ -16,6 +18,7 @@ export class PiecesGroupModel< public readonly pieceMoved$$ = new Subject>(); constructor( + private readonly physics: Physics, public readonly piecesType: type, public readonly piecesColor: color, count: PieceId, @@ -23,13 +26,29 @@ export class PiecesGroupModel< pieces?: Record> ) { super(geometry, undefined, count); + + const physicsProperties = this.physics.addToWorld( + this, + 1 + ) as PhysicsProperties[]; + this.instanceMatrix.setUsage(DynamicDrawUsage); (pieces ? Object.keys(pieces) : Array.from(Array(this.count))).forEach( (pieceKey: number, i) => { const oldPiece = pieces?.[pieceKey]; + const piecePhysicsProperties = physicsProperties[ + i + ] as PhysicsProperties; + const piece = - oldPiece ?? new PieceModel(i, this.piecesType, this.piecesColor); + oldPiece ?? + new PieceModel( + i, + this.piecesType, + this.piecesColor, + piecePhysicsProperties + ); piece.index = i; this.setMatrixAt(i, piece); @@ -72,9 +91,11 @@ export class PiecesGroupModel< // const width = boundingBox.max.x - boundingBox.min.x; const height = boundingBox.max.y - boundingBox.min.y; - this.pieces[id]?.setCoords(board, coords, height / 2); + this.pieces[id]?.setCoords(board, coords, height / 2 + 2); } } + + return this.pieces[id]; } public update() { diff --git a/apps/web/src/core/chess-board/chess-board.component.ts b/apps/web/src/core/chess-board/chess-board.component.ts index b432fe3..f5a323a 100644 --- a/apps/web/src/core/chess-board/chess-board.component.ts +++ b/apps/web/src/core/chess-board/chess-board.component.ts @@ -3,9 +3,9 @@ import "reflect-metadata"; import { inject, singleton } from "tsyringe"; import { Color, InstancedMesh, PlaneGeometry } from "three"; import { PhysicsProperties } from "@chess-d/rapier-physics/dist/types"; +import { Physics } from "@chess-d/rapier-physics"; import { BoardCell } from "../../common"; -import { CoreComponent } from "../core.component"; @singleton() export class ChessBoardComponent { @@ -31,20 +31,18 @@ export class ChessBoardComponent { .clone() .setHex(this.whiteAccent.getHex() * Math.random()); - public physicsBody?: PhysicsProperties; + public physics!: PhysicsProperties; - constructor( - @inject(CoreComponent) private readonly coreComponent: CoreComponent - ) {} + constructor(@inject(Physics) private readonly _physics: Physics) {} public init() { + this.board.name = ChessBoardComponent.name; + this.board.userData = { ...this.board.userData, useBoundingBox: true }; - this.physicsBody = this.coreComponent.physics?.addToWorld( - this.board - ) as PhysicsProperties; + this.physics = this._physics?.addToWorld(this.board) as PhysicsProperties; } } diff --git a/apps/web/src/core/chess-board/chess-board.module.ts b/apps/web/src/core/chess-board/chess-board.module.ts index 3fbe94b..497de81 100644 --- a/apps/web/src/core/chess-board/chess-board.module.ts +++ b/apps/web/src/core/chess-board/chess-board.module.ts @@ -27,7 +27,7 @@ export class ChessBoardModule implements Module { -this.component.halfSize ); - this.component.physicsBody?.rigidBody.setTranslation( + this.component.physics?.rigidBody.setTranslation( { x: this.component.halfSize, y: 0, z: -this.component.halfSize }, true ); diff --git a/apps/web/src/core/core.component.ts b/apps/web/src/core/core.component.ts index 1837c60..b74f520 100644 --- a/apps/web/src/core/core.component.ts +++ b/apps/web/src/core/core.component.ts @@ -1,17 +1,10 @@ import "reflect-metadata"; import { singleton } from "tsyringe"; -import { RapierPhysics } from "@chess-d/rapier-physics"; @singleton() export class CoreComponent { - public physics!: Awaited>; - constructor() {} - public async init() { - this.physics = await RapierPhysics(); - } - public dispose() {} } diff --git a/apps/web/src/core/core.controller.ts b/apps/web/src/core/core.controller.ts index 176fe45..4ea8b8e 100644 --- a/apps/web/src/core/core.controller.ts +++ b/apps/web/src/core/core.controller.ts @@ -1,15 +1,9 @@ import "reflect-metadata"; -import { Subject } from "rxjs"; +import { Subject } from "rxjs"; import { singleton } from "tsyringe"; @singleton() export class CoreController { public readonly gui$$ = new Subject(); - - constructor() {} - - public init() {} - - public dispose() {} } diff --git a/apps/web/src/core/core.module.ts b/apps/web/src/core/core.module.ts index 0c148c0..8a454e6 100644 --- a/apps/web/src/core/core.module.ts +++ b/apps/web/src/core/core.module.ts @@ -8,6 +8,8 @@ import { GameModule } from "./game/game.module"; import { WorldModule } from "./world/world.module"; import { ChessBoardModule } from "./chess-board/chess-board.module"; import { PiecesModule } from "./pieces/pieces.module"; +import { DebugModule } from "./debug/debug.module"; +import { Physics } from "@chess-d/rapier-physics"; @singleton() export class CoreModule implements Module { @@ -22,29 +24,32 @@ export class CoreModule implements Module { @inject(ChessBoardModule) private readonly chessBoardModule: ChessBoardModule, @inject(PiecesModule) - private readonly piecesModule: PiecesModule + private readonly piecesModule: PiecesModule, + @inject(DebugModule) private readonly debugModule: DebugModule, + @inject(Physics) private readonly physics: Physics ) { this.appModule.camera.instance()?.position.set(0, 5, -5); this.appModule.camera.miniCamera()?.position.set(2, 5, -6); + this.init(); + + this.appModule.timer.step$().subscribe(() => { + this.physics.step(); + }); + self.onmessage = (e: MessageEvent) => { if ((e.data?.type as string)?.startsWith("pawn")) controller.gui$$.next(e.data); }; - - this.appModule.timer.step$().subscribe(() => { - this.component.physics?.step(); - }); - this.init(); } - public async init() { - await this.component.init(); + public init() { this.resourceModule.init(); this.worldModule.init(); this.gameModule.init(); this.chessBoardModule.init(); this.piecesModule.init(); + this.debugModule.init(); } public dispose() { @@ -54,5 +59,6 @@ export class CoreModule implements Module { this.chessBoardModule.dispose(); this.piecesModule.dispose(); this.appModule.dispose(); + this.debugModule.dispose(); } } diff --git a/apps/web/src/core/core.util.ts b/apps/web/src/core/core.util.ts index cc22008..694bdb1 100644 --- a/apps/web/src/core/core.util.ts +++ b/apps/web/src/core/core.util.ts @@ -1,13 +1,16 @@ import { container } from "tsyringe"; import { isObject } from "@quick-threejs/utils"; import { AppModule } from "@quick-threejs/reactive"; +import { Physics, RapierPhysics } from "@chess-d/rapier-physics"; import { CoreModule } from "./core.module"; -export const setupCoreModule = (app: AppModule) => { +export const setupCoreModule = async (app: AppModule) => { if (!isObject(app)) throw new Error("Unable to retrieve the application context."); container.register(AppModule, { useValue: app }); + container.register(Physics, { useValue: await RapierPhysics() }); + return container.resolve(CoreModule); }; diff --git a/apps/web/src/core/debug/debug.component.ts b/apps/web/src/core/debug/debug.component.ts new file mode 100644 index 0000000..75dbed8 --- /dev/null +++ b/apps/web/src/core/debug/debug.component.ts @@ -0,0 +1,10 @@ +import { BufferGeometry, LineSegments, MeshBasicMaterial } from "three"; +import { singleton } from "tsyringe"; + +@singleton() +export class DebugComponent { + public readonly lines = new LineSegments( + new BufferGeometry(), + new MeshBasicMaterial({ color: 0xff40f0 }) + ); +} diff --git a/apps/web/src/core/debug/debug.controller.ts b/apps/web/src/core/debug/debug.controller.ts new file mode 100644 index 0000000..dc806c5 --- /dev/null +++ b/apps/web/src/core/debug/debug.controller.ts @@ -0,0 +1,24 @@ +import { inject, singleton } from "tsyringe"; +import { filter, map, Observable } from "rxjs"; +import { AppModule } from "@quick-threejs/reactive"; +import { Physics } from "@chess-d/rapier-physics"; + +import { DebugComponent } from "./debug.component"; + +@singleton() +export class DebugController { + public readonly physicsDebugRender$: Observable< + InstanceType + >; + + constructor( + @inject(AppModule) private readonly appModule: AppModule, + @inject(DebugComponent) private readonly component: DebugComponent, + @inject(Physics) private readonly _physics: Physics + ) { + this.physicsDebugRender$ = this.appModule.timer.step$().pipe( + filter(() => !!this.appModule?.debug?.enabled), + map(() => this._physics.world.debugRender()) + ); + } +} diff --git a/apps/web/src/core/debug/debug.module.ts b/apps/web/src/core/debug/debug.module.ts new file mode 100644 index 0000000..74550c3 --- /dev/null +++ b/apps/web/src/core/debug/debug.module.ts @@ -0,0 +1,37 @@ +import { inject, singleton } from "tsyringe"; +import { BufferAttribute } from "three"; +import { AppModule, Module } from "@quick-threejs/reactive"; + +import { DebugComponent } from "./debug.component"; +import { DebugController } from "./debug.controller"; + +@singleton() +export class DebugModule implements Module { + constructor( + @inject(DebugComponent) private readonly component: DebugComponent, + @inject(DebugController) private readonly controller: DebugController, + @inject(AppModule) private readonly appModule: AppModule + ) { + if (this.appModule.debug.enabled()) + appModule.world.scene().add(this.component.lines); + + this.controller.physicsDebugRender$.subscribe({ + next: (buffers) => { + this.component.lines.geometry.setAttribute( + "position", + new BufferAttribute(buffers.vertices, 3) + ); + this.component.lines.geometry.setAttribute( + "color", + new BufferAttribute(buffers.colors, 4) + ); + } + }); + } + + init(): void {} + + dispose(): void { + throw new Error("Method not implemented."); + } +} diff --git a/apps/web/src/core/pieces/pieces.component.ts b/apps/web/src/core/pieces/pieces.component.ts index b90f820..69648aa 100644 --- a/apps/web/src/core/pieces/pieces.component.ts +++ b/apps/web/src/core/pieces/pieces.component.ts @@ -1,4 +1,6 @@ import { inject, singleton } from "tsyringe"; +import { BufferGeometry } from "three"; +import { Physics } from "@chess-d/rapier-physics"; import { PiecesGroupModel, @@ -8,8 +10,6 @@ import { } from "../../common"; import { ChessBoardComponent } from "../chess-board/chess-board.component"; import { ResourceComponent } from "../resource/resource.component"; -import { CoreComponent } from "../core.component"; -import { BufferGeometry } from "three"; @singleton() export class PiecesComponent { @@ -20,7 +20,7 @@ export class PiecesComponent { private readonly chessBoardComponent: ChessBoardComponent, @inject(ResourceComponent) private readonly resourceComponent: ResourceComponent, - @inject(CoreComponent) private readonly coreComponent: CoreComponent + @inject(Physics) private readonly _physics: Physics ) {} private _initPawns(color: Color) { @@ -188,7 +188,7 @@ export class PiecesComponent { pieces?: PiecesGroupModel["pieces"] ) { return new PiecesGroupModel( - this.coreComponent.physics, + this._physics, type, color, count, diff --git a/apps/web/src/main.ts b/apps/web/src/main.ts index 04119de..79259d0 100644 --- a/apps/web/src/main.ts +++ b/apps/web/src/main.ts @@ -7,7 +7,7 @@ import "./assets/styles/main.css"; register({ location: new URL("./main.worker.ts", import.meta.url) as unknown as string, - enableDebug: true, + enableDebug: !!import.meta.env?.DEV, axesSizes: 5, gridSizes: 10, withMiniCamera: true, diff --git a/apps/web/turbo.json b/apps/web/turbo.json new file mode 100644 index 0000000..868ec9f --- /dev/null +++ b/apps/web/turbo.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://turborepo.org/schema.json", + "extends": ["//"], + "tasks": { + "dev": { + "env": ["DEV"] + } + } +}