Capture current mission, AI, map and administration development
Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
"""Execute actual camera sample validation/interpolation; native math substituted."""
|
||||
import re
|
||||
from policy_harness import ROOT,body,run
|
||||
p=ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_ObserverPlayerView.c'
|
||||
def adapt(s):
|
||||
s=s.replace('vector geometry = "0 0 0"', 'vector geometry = {}')
|
||||
for name in ['ERR_ObserverScope','ERR_ObserverViewRules','Math','Math3D','vector','RplId']:s=s.replace(name+'.',name+'::')
|
||||
s=s.replace('out vector','vector').replace('out float ','float& ')
|
||||
s=re.sub(r'ERR_ObserverViewSample (\w+)', r'ERR_ObserverViewSample* \1',s)
|
||||
s=re.sub(r'(sample|start|end)\.', r'\1->',s)
|
||||
return re.sub(r'(m_Samples\[\d+\])\.',r'\1->',s)
|
||||
rules='\n'.join(adapt(body(p,k)) for k in ['static bool Sample(', 'static bool Fresh('])
|
||||
methods='\n'.join(adapt(body(p,k)) for k in ['void Clear(', 'void Push(', 'bool Read(', 'float Playback(', 'void Align('])
|
||||
baseline=ROOT/'docs/evidence/observer-smoothing-2026-10-09/baseline/addons/core/Scripts/Game/ERR/00_Core/ERR_ObserverPlayerView.c'
|
||||
old_methods='\n'.join(adapt(body(baseline,k)) for k in ['void Clear(', 'void Push(', 'bool Read('])
|
||||
run(r'''
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
struct vector {float operator[](int i){return i==0?x:(i==1?y:z);}bool operator==(vector b){return x==b.x&&y==b.y&&z==b.z;}static vector Zero;float x=0,y=0,z=0;vector operator+(vector v){return{x+v.x,y+v.y,z+v.z};}vector operator-(vector v){return{x-v.x,y-v.y,z-v.z};}vector operator*(float n){return{x*n,y*n,z*n};}
|
||||
float LengthSq(){return x*x+y*y+z*z;}void Normalize(){float n=sqrt(LengthSq());x/=n;y/=n;z/=n;}
|
||||
static float Dot(vector a,vector b){return a.x*b.x+a.y*b.y+a.z*b.z;}static vector Lerp(vector a,vector b,float f){return a+(b-a)*f;}
|
||||
};
|
||||
vector vector::Zero;
|
||||
namespace Math {float Round(float f){return std::round(f);}float AbsFloat(float f){return fabs(f);}float Max(float a,float b){return std::max(a,b);}float Min(float a,float b){return std::min(a,b);}float Clamp(float n,float a,float b){return std::clamp(n,a,b);}float Lerp(float a,float b,float f){return a+(b-a)*f;}}
|
||||
vector cross(vector a,vector b){return{a.y*b.z-a.z*b.y,a.z*b.x-a.x*b.z,a.x*b.y-a.y*b.x};}
|
||||
namespace Math3D {void DirectionAndUpMatrix(vector forward,vector up,vector* out){auto right=cross(up,forward);right.Normalize();out[0]=right;out[1]=cross(forward,right);out[2]=forward;}}
|
||||
struct RplId {int value=-1;RplId(int v=-1):value(v){}static RplId Invalid(){return{};}bool operator==(RplId a){return value==a.value;}bool operator!=(RplId a){return value!=a.value;}};
|
||||
struct ERR_ObserverViewRules {'''+rules+r'''};
|
||||
struct ERR_ObserverScope {'''+adapt(body(ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_ObserverScope.c','static bool Valid('))+r'''};
|
||||
struct ERR_ObserverViewSample {int Stamp;vector Position,Forward,Up;float Fov,Near,PipFov=0,Vignette=0;vector Geometry;};
|
||||
template<class T>struct Array:std::vector<T>{int Count(){return this->size();}bool IsEmpty(){return this->empty();}void Clear(){this->clear();}void Insert(T v){this->push_back(v);}void Remove(int i){this->erase(this->begin()+i);}};
|
||||
struct Buffer {RplId m_ScopeWeapon;vector m_ScopeData;RplId m_Target;int m_Stamp=-1;float m_Received=-1,m_Playback=0,m_ReadAt=0,m_PipFov=0,m_Vignette=0;vector m_Geometry;Array<ERR_ObserverViewSample*> m_Samples;
|
||||
'''+methods+r'''};
|
||||
struct Baseline {RplId m_ScopeWeapon;vector m_ScopeData;RplId m_Target;int m_Stamp=-1;float m_Received=-1;vector m_Position,m_Forward,m_Up,m_StartPosition,m_StartForward,m_StartUp;float m_Fov=0,m_Near=0,m_StartFov=0,m_Blend=1;
|
||||
'''+old_methods+r'''};
|
||||
bool close(float a,float b){return fabs(a-b)<0.0001;}
|
||||
int main(){
|
||||
vector pos{.1,1.6,.2},forward{0,0,1},up{0,1,0};
|
||||
assert(ERR_ObserverViewRules::Sample(pos,forward,up,60,.0125));
|
||||
assert(!ERR_ObserverViewRules::Sample({31,0,0},forward,up,60,.0125));
|
||||
assert(!ERR_ObserverViewRules::Sample(pos,{},up,60,.0125));
|
||||
assert(!ERR_ObserverViewRules::Sample(pos,forward,forward,60,.0125));
|
||||
float nan=std::numeric_limits<float>::quiet_NaN();
|
||||
assert(!ERR_ObserverViewRules::Sample({nan,0,0},forward,up,60,.0125));
|
||||
assert(!ERR_ObserverViewRules::Sample(pos,{nan,0,1},up,60,.0125));
|
||||
assert(!ERR_ObserverViewRules::Sample(pos,forward,up,nan,.0125));
|
||||
assert(!ERR_ObserverViewRules::Sample(pos,forward,up,60,nan));
|
||||
assert(!ERR_ObserverViewRules::Sample(pos,forward,up,0,.0125));
|
||||
Buffer b;vector matrix[4];float fov,nearPlane;
|
||||
assert(!b.Read(1,100,1,matrix,fov,nearPlane));
|
||||
b.Push(1,100,100,pos,forward,up,60,.0125, -1, {});assert(b.Read(1,100,.01,matrix,fov,nearPlane));assert(close(matrix[3].y,1.6)&&close(fov,60));
|
||||
// Actual lean/roll and zoom, without forcing world-up or a hard-coded camera FOV.
|
||||
vector leanedUp{.6,.8,0};b.Push(1,150,150,{.3,1.4,.2},forward,leanedUp,30,.02, -1, {});
|
||||
assert(b.Read(1,225,.025,matrix,fov,nearPlane));assert(close(matrix[3].x,.2)&&close(fov,45)&&matrix[1].x>0);
|
||||
assert(b.Read(1,250,.025,matrix,fov,nearPlane));assert(close(matrix[3].x,.3)&&close(fov,30)&&close(matrix[1].x,.6)&&close(nearPlane,.02));
|
||||
b.Push(1,149,270,{9,9,9},forward,up,80,.1, -1, {}); // out-of-order packet cannot rewind the view
|
||||
assert(b.Read(1,275,.025,matrix,fov,nearPlane)&&close(matrix[3].x,.3));
|
||||
for(int i=0;i<1000;i++)assert(b.Read(1,300,.016,matrix,fov,nearPlane)&&close(matrix[3].x,.3)); // no integration drift
|
||||
assert(!b.Read(1,501,.01,matrix,fov,nearPlane)); // gap beyond 350 ms, no stale head approximation
|
||||
assert(!b.Read(2,250,.01,matrix,fov,nearPlane)); // target switch never inherits previous pose
|
||||
b.Push(2,200,600,{1,2,3},forward,up,75,.1, -1, {});assert(b.Read(2,600,0,matrix,fov,nearPlane)&&close(matrix[3].x,1));
|
||||
b.Clear();assert(!b.Read(2,600,0,matrix,fov,nearPlane));
|
||||
b.Push(1,700,700,pos,forward,up,12,.02,22,{1,0,1});
|
||||
assert(b.Read(1,700,0,matrix,fov,nearPlane)&&close(fov,12)&&b.m_ScopeWeapon==RplId(22));
|
||||
b.Push(1,701,710,pos,forward,up,60,.02,22,{nan,0,1});
|
||||
assert(b.m_Stamp==700&&b.m_ScopeData[0]==1);
|
||||
b.Push(1,702,720,pos,forward,up,60,.02,-1,{});
|
||||
assert(b.Read(1,720,0,matrix,fov,nearPlane)&&close(fov,60)&&b.m_ScopeWeapon==RplId(-1));
|
||||
assert(!ERR_ObserverScope::Valid(-1,{1,0,0}));
|
||||
assert(!ERR_ObserverScope::Valid(22,{1.5,0,0}));
|
||||
assert(!ERR_ObserverScope::Valid(22,{1,91,0}));
|
||||
assert(!ERR_ObserverScope::Valid(22,{1,0,2}));
|
||||
assert(!ERR_ObserverViewRules::Fresh(100,101));
|
||||
// Real interpolation bodies, same constant-speed poses, uneven packet arrivals.
|
||||
// Different client/server clock origins, dropped packets and varied render FPS.
|
||||
for(float frame:{1000.f/30,1000.f/60,1000.f/144}){
|
||||
Buffer smooth;Baseline old;int packet=0;float prevNew=0,prevOld=0,lastNewStep=0,lastOldStep=0;
|
||||
float newVariation=0,oldVariation=0;int delay[]={0,25,5,35,0,15};
|
||||
for(float t=0;t<2500;t+=frame){
|
||||
while(packet<=50&&packet*50+delay[packet%6]<=t){
|
||||
if(packet%13!=12){ // occasional single-packet loss
|
||||
vector location{packet*.05f,1.6,0};
|
||||
smooth.Push(1,70000+packet*50,10000+t,location,forward,up,60,.02,-1,{});
|
||||
old.Push(1,70000+packet*50,10000+t,location,forward,up,60,.02,-1,{});
|
||||
}
|
||||
packet++;
|
||||
}
|
||||
assert(smooth.Read(1,10000+t,frame/1000,matrix,fov,nearPlane));float nextNew=matrix[3].x;
|
||||
assert(old.Read(1,10000+t,frame/1000,matrix,fov,nearPlane));float nextOld=matrix[3].x;
|
||||
float newStep=nextNew-prevNew,oldStep=nextOld-prevOld;
|
||||
assert(newStep>=-0.0001); // no timeline rewind
|
||||
if(t>500){newVariation+=fabs(newStep-lastNewStep);oldVariation+=fabs(oldStep-lastOldStep);}
|
||||
prevNew=nextNew;prevOld=nextOld;lastNewStep=newStep;lastOldStep=oldStep;
|
||||
assert(smooth.m_Samples.Count()<=12);
|
||||
}
|
||||
std::cout<<"frame_ms="<<frame<<" buffered_step_variation="<<newVariation<<" baseline="<<oldVariation<<"\n";
|
||||
assert(newVariation<oldVariation*.25f);
|
||||
}
|
||||
// PiP zoom/parallax/shading must use the same delayed time as the camera.
|
||||
Buffer paired;
|
||||
paired.Push(1,100,100,pos,forward,up,12,.02,22,{1,0,0},6,{0,0,1},.8);
|
||||
paired.Push(1,150,150,pos,forward,up,6,.02,22,{1,0,0},3,{2,4,2},1.2);
|
||||
assert(paired.Read(1,225,.016,matrix,fov,nearPlane));
|
||||
assert(close(fov,9)&&close(paired.m_PipFov,4.5)&&close(paired.m_Geometry.x,1)&&close(paired.m_Vignette,1));
|
||||
// A temporarily unresolved optic may skip eye reads. Rejoining uses the
|
||||
// aiming playhead exactly, rather than an independently restarted delay.
|
||||
Buffer aim,eye;
|
||||
for(int i=0;i<20;i++){
|
||||
aim.Push(1,i*50,i*50,{i*.1f,1.6,0},forward,up,12,.02,22,{1,0,0});
|
||||
eye.Push(1,i*50,i*50,{i*.1f,1.6,0},forward,up,60,.02,22,{1,0,0});
|
||||
assert(aim.Read(1,i*50,.05,matrix,fov,nearPlane));
|
||||
}
|
||||
assert(aim.Read(1,975,.025,matrix,fov,nearPlane));float aimX=matrix[3].x;
|
||||
eye.Align(aim.Playback(),975);assert(eye.Read(1,975,.025,matrix,fov,nearPlane));assert(close(aimX,matrix[3].x));
|
||||
// Holding an unrendered stream stays bounded; opening it resumes near live.
|
||||
Buffer parked;
|
||||
for(int i=0;i<1000;i++)parked.Push(1,i*50,i*50,pos,forward,up,60,.02,-1,{});
|
||||
assert(parked.m_Samples.Count()==12);
|
||||
assert(parked.Read(1,49950,0,matrix,fov,nearPlane)&&close(matrix[3].x,pos.x));
|
||||
// Scope changes, reacquisition after expiry and target changes snap to their
|
||||
// own pose instead of blending through another sight or another player.
|
||||
parked.Push(1,50000,50400,{2,1,0},forward,up,30,.02,-1,{});
|
||||
assert(parked.Read(1,50400,0,matrix,fov,nearPlane)&&close(matrix[3].x,2));
|
||||
parked.Clear();assert(parked.m_Samples.IsEmpty()&&parked.m_ScopeWeapon==RplId(-1));
|
||||
}
|
||||
''')
|
||||
print('PASS: actual view buffer validates samples, follows position/roll/FOV, rejects stale/foreign/reordered data, buffers jitter without drift, bounds memory and expires gaps')
|
||||
Reference in New Issue
Block a user