From bf572d2301eb5d2435fe3d51bb40c8c88ec3c55b Mon Sep 17 00:00:00 2001 From: toloudis Date: Mon, 9 Oct 2023 17:14:51 -0700 Subject: [PATCH] add translation and rotation to fiber agents, and a single fiber test that translates and rotates a fiber --- examples/CurveSimulator.ts | 2 + examples/MetaballSimulator.ts | 2 + examples/PdbSimulator.ts | 2 + examples/PointSimulator.ts | 2 + examples/SingleCurveSimulator.ts | 160 ++++++++++++++++++ examples/Viewer.tsx | 9 + src/visGeometry/VisAgent.ts | 13 +- src/visGeometry/index.ts | 3 + src/visGeometry/rendering/InstancedFiber.ts | 27 +++ .../rendering/InstancedFiberShader.ts | 7 + 10 files changed, 221 insertions(+), 6 deletions(-) create mode 100644 examples/SingleCurveSimulator.ts diff --git a/examples/CurveSimulator.ts b/examples/CurveSimulator.ts index 60b85f02..bb84a356 100644 --- a/examples/CurveSimulator.ts +++ b/examples/CurveSimulator.ts @@ -176,4 +176,6 @@ export default class CurveSim implements IClientSimulatorImpl { }, }; } + + updateSimulationState(data: Record) {} } diff --git a/examples/MetaballSimulator.ts b/examples/MetaballSimulator.ts index 19b26d77..06c33681 100644 --- a/examples/MetaballSimulator.ts +++ b/examples/MetaballSimulator.ts @@ -182,4 +182,6 @@ export default class MetaballSimulator implements IClientSimulatorImpl { }, }; } + + updateSimulationState(data: Record) {} } diff --git a/examples/PdbSimulator.ts b/examples/PdbSimulator.ts index d29e1eda..5ff7d99d 100644 --- a/examples/PdbSimulator.ts +++ b/examples/PdbSimulator.ts @@ -214,4 +214,6 @@ export default class PdbSim implements IClientSimulatorImpl { }, }; } + + updateSimulationState(data: Record) {} } diff --git a/examples/PointSimulator.ts b/examples/PointSimulator.ts index d0c82996..b0646eee 100644 --- a/examples/PointSimulator.ts +++ b/examples/PointSimulator.ts @@ -141,4 +141,6 @@ export default class PointSim implements IClientSimulatorImpl { }, }; } + + updateSimulationState(data: Record) {} } diff --git a/examples/SingleCurveSimulator.ts b/examples/SingleCurveSimulator.ts new file mode 100644 index 00000000..037dd891 --- /dev/null +++ b/examples/SingleCurveSimulator.ts @@ -0,0 +1,160 @@ +import { + IClientSimulatorImpl, + ClientMessageEnum, +} from "../src/simularium/localSimulators/IClientSimulatorImpl"; +import { + EncodedTypeMapping, + TrajectoryFileInfo, + VisDataMessage, +} from "../src/simularium/types"; +import VisTypes from "../src/simularium/VisTypes"; +import { DEFAULT_CAMERA_SPEC } from "../src/constants"; + +export default class SingleCurveSim implements IClientSimulatorImpl { + curveData: number[]; + nPointsPerCurve: number; + currentFrame: number; + + constructor() { + this.nPointsPerCurve = 5; + this.curveData = this.makeCurveBundle(1, this.nPointsPerCurve); + this.currentFrame = 0; + } + + private randomFloat(min, max) { + if (max === undefined) { + max = min; + min = 0; + } + return Math.random() * (max - min) + min; + } + + private randomSpherePoint(x0, y0, z0, radius): number[] { + const u = Math.random(); + const v = Math.random(); + const theta = 2 * Math.PI * u; + const phi = Math.acos(2 * v - 1); + const x = x0 + radius * Math.sin(phi) * Math.cos(theta); + const y = y0 + radius * Math.sin(phi) * Math.sin(theta); + const z = z0 + radius * Math.cos(phi); + return [x, y, z]; + } + private randomPtInBox(xmin, xmax, ymin, ymax, zmin, zmax) { + return [ + this.randomFloat(xmin, xmax), + this.randomFloat(ymin, ymax), + this.randomFloat(zmin, zmax), + ]; + } + + private makeCurveBundle() { + // make one curve w/5 pts + const curves: number[] = []; + let p: number[]; + p = this.randomPtInBox(-4, -3, -2, 2, -2, 2); + curves.push(p[0]); + curves.push(p[1]); + curves.push(p[2]); + p = this.randomPtInBox(-2.5, -2, -1, 1, -1, 1); + curves.push(p[0]); + curves.push(p[1]); + curves.push(p[2]); + p = this.randomPtInBox(-1, 1, -0.5, 0.5, -0.5, 0.5); + curves.push(p[0]); + curves.push(p[1]); + curves.push(p[2]); + p = this.randomPtInBox(2, 2.5, -1, 1, -1, 1); + curves.push(p[0]); + curves.push(p[1]); + curves.push(p[2]); + p = this.randomPtInBox(3, 4, -2, 2, -2, 2); + curves.push(p[0]); + curves.push(p[1]); + curves.push(p[2]); + return curves; + } + + public update(_dt: number): VisDataMessage { + const nFloatsPerCurve = this.nPointsPerCurve * 3; + //const dt_adjusted = dt / 1000; + const amplitude = 0.05; + for (let jj = 0; jj < this.nPointsPerCurve; ++jj) { + this.curveData[jj * 3 + 0] += this.randomFloat( + -amplitude, + amplitude + ); + this.curveData[jj * 3 + 1] += this.randomFloat( + -amplitude, + amplitude + ); + this.curveData[jj * 3 + 2] += this.randomFloat( + -amplitude, + amplitude + ); + } + // fill agent data. + const agentData: number[] = []; + agentData.push(VisTypes.ID_VIS_TYPE_FIBER); // vis type + agentData.push(0); // instance id + agentData.push(0); // type + agentData.push(3.0 * Math.sin(this.currentFrame / 50)); // x + agentData.push(0); // y + agentData.push(0); // z + agentData.push(0); // rx + agentData.push(0); // ry + agentData.push(this.currentFrame / 50); // rz + agentData.push(0.3); // collision radius + agentData.push(nFloatsPerCurve); + for (let jj = 0; jj < nFloatsPerCurve; ++jj) { + agentData.push(this.curveData[jj]); + } + + const frameData: VisDataMessage = { + // TODO get msgType and connId out of here + msgType: ClientMessageEnum.ID_VIS_DATA_ARRIVE, + bundleStart: this.currentFrame, + bundleSize: 1, // frames + bundleData: [ + { + data: agentData, + frameNumber: this.currentFrame, + time: this.currentFrame, + }, + ], + fileName: "hello world", + }; + this.currentFrame++; + return frameData; + } + + public getInfo(): TrajectoryFileInfo { + const typeMapping: EncodedTypeMapping = {}; + typeMapping[0] = { name: "fiber0" }; + return { + // TODO get msgType and connId out of here + connId: "hello world", + msgType: ClientMessageEnum.ID_TRAJECTORY_FILE_INFO, + version: 2, + timeStepSize: 1, + totalSteps: 1000, + // bounding volume dimensions + size: { + x: 12, + y: 12, + z: 12, + }, + cameraDefault: DEFAULT_CAMERA_SPEC, + typeMapping: typeMapping, + spatialUnits: { + magnitude: 1, + name: "m", + }, + timeUnits: { + magnitude: 1, + name: "s", + }, + }; + } + + updateSimulationState(data: Record) {} +} diff --git a/examples/Viewer.tsx b/examples/Viewer.tsx index 45db80fb..7ffcb3c7 100644 --- a/examples/Viewer.tsx +++ b/examples/Viewer.tsx @@ -20,6 +20,7 @@ import PointSimulatorLive from "./PointSimulatorLive"; import PdbSimulator from "./PdbSimulator"; import SinglePdbSimulator from "./SinglePdbSimulator"; import CurveSimulator from "./CurveSimulator"; +import SingleCurveSimulator from "./SingleCurveSimulator"; import ColorPicker from "./ColorPicker"; import { SMOLDYN_TEMPLATE, @@ -536,6 +537,13 @@ class Viewer extends React.Component { }, playbackFile ); + } else if (playbackFile === "TEST_SINGLE_FIBER") { + simulariumController.changeFile( + { + clientSimulator: new SingleCurveSimulator(), + }, + playbackFile + ); } else if (playbackFile === "TEST_PDB") { simulariumController.changeFile( { @@ -671,6 +679,7 @@ class Viewer extends React.Component { + diff --git a/src/visGeometry/VisAgent.ts b/src/visGeometry/VisAgent.ts index 8c6c3d61..c7293968 100644 --- a/src/visGeometry/VisAgent.ts +++ b/src/visGeometry/VisAgent.ts @@ -155,17 +155,18 @@ export default class VisAgent { } public getFollowPosition(): Vector3 { + const pos = new Vector3( + this.agentData.x, + this.agentData.y, + this.agentData.z + ); if ( this.agentData["vis-type"] === VisTypes.ID_VIS_TYPE_FIBER && this.fiberCurve ) { - return this.fiberCurve.getPoint(0.5); + return this.fiberCurve.getPoint(0.5).add(pos); } else { - return new Vector3( - this.agentData.x, - this.agentData.y, - this.agentData.z - ); + return pos; } } } diff --git a/src/visGeometry/index.ts b/src/visGeometry/index.ts index c37ab2e4..a77d2760 100644 --- a/src/visGeometry/index.ts +++ b/src/visGeometry/index.ts @@ -1574,6 +1574,9 @@ class VisGeometry { agentData.y, agentData.z, agentData.cr * scale * 0.5, + agentData.xrot, + agentData.yrot, + agentData.zrot, visAgent.agentData.instanceId, visAgent.signedTypeId() ); diff --git a/src/visGeometry/rendering/InstancedFiber.ts b/src/visGeometry/rendering/InstancedFiber.ts index 2343b814..a8919a40 100644 --- a/src/visGeometry/rendering/InstancedFiber.ts +++ b/src/visGeometry/rendering/InstancedFiber.ts @@ -2,9 +2,11 @@ import { BufferAttribute, BufferGeometry, CylinderGeometry, + Euler, InstancedBufferAttribute, InstancedBufferGeometry, Mesh, + Quaternion, DataTexture, RGBAFormat, FloatType, @@ -21,6 +23,9 @@ import { updateProjectionMatrix, } from "./MultipassMaterials"; +const tmpQuaternion = new Quaternion(); +const tmpEuler = new Euler(); + function createTubeGeometry( numSides = 8, subdivisions = 50, @@ -128,6 +133,7 @@ class InstancedFiber { private instancedGeometry: InstancedBufferGeometry; private positionAttribute: InstancedBufferAttribute; // x,y,z,scale + private rotationAttribute: InstancedBufferAttribute; // quaternion private instanceAttribute: InstancedBufferAttribute; // instance id, type id (color index) // holds control points for all the curves @@ -156,6 +162,10 @@ class InstancedFiber { Uint8Array.from([]), 1 ); + this.rotationAttribute = new InstancedBufferAttribute( + Uint8Array.from([]), + 1 + ); this.instanceAttribute = new InstancedBufferAttribute( Uint8Array.from([]), 1 @@ -242,6 +252,11 @@ class InstancedFiber { this.positionAttribute ); + const newRot = new Float32Array(4 * n); + newRot.set(this.rotationAttribute.array); + this.rotationAttribute = new InstancedBufferAttribute(newRot, 4, false); + this.instancedGeometry.setAttribute("rotation", this.rotationAttribute); + const newInst = new Float32Array(3 * n); newInst.set(this.instanceAttribute.array); this.instanceAttribute = new InstancedBufferAttribute( @@ -290,12 +305,17 @@ class InstancedFiber { y: number, z: number, scale: number, + rx: number, + ry: number, + rz: number, uniqueAgentId: number, typeId: number ): void { const offset = this.currentInstance; this.checkRealloc(this.currentInstance + 1); this.positionAttribute.setXYZW(offset, x, y, z, scale); + const q = tmpQuaternion.setFromEuler(tmpEuler.set(rx, ry, rz)); + this.rotationAttribute.setXYZW(offset, q.x, q.y, q.z, q.w); this.instanceAttribute.setXYZ( offset, uniqueAgentId, @@ -320,6 +340,7 @@ class InstancedFiber { // assumes the entire buffers are invalidated. this.instanceAttribute.needsUpdate = true; this.positionAttribute.needsUpdate = true; + this.rotationAttribute.needsUpdate = true; this.curveData.needsUpdate = true; this.isUpdating = false; } @@ -373,6 +394,9 @@ class InstancedFiberGroup { y: number, z: number, scale: number, + rx: number, + ry: number, + rz: number, uniqueAgentId: number, typeId: number ): void { @@ -386,6 +410,9 @@ class InstancedFiberGroup { y, z, scale, + rx, + ry, + rz, uniqueAgentId, typeId ); diff --git a/src/visGeometry/rendering/InstancedFiberShader.ts b/src/visGeometry/rendering/InstancedFiberShader.ts index 9db68180..ffe39944 100644 --- a/src/visGeometry/rendering/InstancedFiberShader.ts +++ b/src/visGeometry/rendering/InstancedFiberShader.ts @@ -12,6 +12,7 @@ in vec2 uv; // per instance attributes in vec4 translateAndScale; // xyz trans, w scale +in vec4 rotation; // quaternion // instanceID, typeId, and which row of texture contains this curve in vec3 instanceAndTypeId; @@ -305,6 +306,10 @@ void createTube (float t, vec2 volume, out vec3 offset, out vec3 normal) { } #endif +vec3 applyQuaternionToVector( vec4 q, vec3 v ) { + return v + 2.0 * cross( q.xyz, cross( q.xyz, v ) + q.w * v ); +} + void main() { // load the curve for (int i = 0; i < NUM_POINTS; ++i) { @@ -329,6 +334,8 @@ void main() { // vUv = uv.yx; // swizzle this to match expectations // project our vertex position + transformed = applyQuaternionToVector(rotation, transformed); + transformed += translateAndScale.xyz; vec4 mvPosition = modelViewMatrix * vec4(transformed, 1.0); IN_viewPos = mvPosition.xyz;