<?php
class EntityInterpolationSystem { private $entityStates; private $interpolationDelay = 0.1; private $renderBuffer; public function __construct() { $this->entityStates = []; $this->renderBuffer = []; }
public function receiveEntityUpdate($entityId, $newState, $serverTime) { if (!isset($this->entityStates[$entityId])) { $this->entityStates[$entityId] = new CircularBuffer(10); } $this->entityStates[$entityId]->push([ 'state' => $newState, 'timestamp' => $serverTime, 'receive_time' => microtime(true) ]); }
public function updateRenderStates($currentTime) { $renderTime = $currentTime - $this->interpolationDelay; foreach ($this->entityStates as $entityId => $stateBuffer) { if ($stateBuffer->size() < 2) { continue; } $interpolationData = $this->findInterpolationInterval( $stateBuffer, $renderTime ); if ($interpolationData) { $interpolatedState = $this->interpolateStates( $interpolationData['start'], $interpolationData['end'], $interpolationData['factor'] ); $this->renderBuffer[$entityId] = $interpolatedState; } } }
private function findInterpolationInterval($stateBuffer, $targetTime) { $states = $stateBuffer->toArray(); for ($i = 0; $i < count($states) - 1; $i++) { $startTime = $states[$i]['timestamp']; $endTime = $states[$i + 1]['timestamp']; if ($targetTime >= $startTime && $targetTime <= $endTime) { $timeRange = $endTime - $startTime; if ($timeRange > 0) { $factor = ($targetTime - $startTime) / $timeRange; return [ 'start' => $states[$i]['state'], 'end' => $states[$i + 1]['state'], 'factor' => $factor ]; } } } return null; }
private function interpolateStates($startState, $endState, $factor) { $interpolated = clone $startState; $interpolated->position['x'] = $this->lerp( $startState->position['x'], $endState->position['x'], $factor ); $interpolated->position['y'] = $this->lerp( $startState->position['y'], $endState->position['y'], $factor ); $interpolated->position['z'] = $this->lerp( $startState->position['z'], $endState->position['z'], $factor ); $interpolated->rotation = $this->slerp( $startState->rotation, $endState->rotation, $factor ); return $interpolated; } private function lerp($a, $b, $f) { return $a + $f * ($b - $a); } private function slerp($q1, $q2, $t) { $dot = $this->quaternionDot($q1, $q2); if ($dot < 0) { $q2 = $this->quaternionNegate($q2); $dot = -$dot; } if ($dot > 0.9995) { return $this->quaternionNormalize( $this->quaternionLerp($q1, $q2, $t) ); } $theta_0 = acos($dot); $theta = $theta_0 * $t; $sin_theta = sin($theta); $sin_theta_0 = sin($theta_0); $s1 = cos($theta) - $dot * $sin_theta / $sin_theta_0; $s2 = $sin_theta / $sin_theta_0; return $this->quaternionAdd( $this->quaternionScale($q1, $s1), $this->quaternionScale($q2, $s2) ); }
public function getRenderState($entityId) { return isset($this->renderBuffer[$entityId]) ? $this->renderBuffer[$entityId] : null; } }
class CircularBuffer { private $buffer; private $capacity; private $size; private $head; public function __construct($capacity) { $this->buffer = []; $this->capacity = $capacity; $this->size = 0; $this->head = 0; } public function push($item) { if ($this->size < $this->capacity) { $this->buffer[] = $item; $this->size++; } else { $this->buffer[$this->head] = $item; $this->head = ($this->head + 1) % $this->capacity; } } public function toArray() { if ($this->size < $this->capacity) { return $this->buffer; } $result = []; for ($i = 0; $i < $this->capacity; $i++) { $index = ($this->head + $i) % $this->capacity; $result[] = $this->buffer[$index]; } return $result; } public function size() { return $this->size; } }
?>
|