osc multi arguments
This commit is contained in:
149
OSC/Test.json
149
OSC/Test.json
@@ -68,23 +68,28 @@
|
|||||||
"padding": "auto",
|
"padding": "auto",
|
||||||
"html": "",
|
"html": "",
|
||||||
"css": "",
|
"css": "",
|
||||||
"design": "default",
|
|
||||||
"pips": true,
|
"pips": true,
|
||||||
"snap": false,
|
"snap": false,
|
||||||
"spring": false,
|
"spring": false,
|
||||||
"rangeX": {
|
"rangeX": {
|
||||||
"min": -180,
|
"min": -60,
|
||||||
"max": 180
|
"max": 60
|
||||||
},
|
},
|
||||||
"rangeY": {
|
"rangeY": {
|
||||||
"min": -120,
|
"min": 45,
|
||||||
"max": 120
|
"max": -45
|
||||||
},
|
},
|
||||||
"logScaleX": false,
|
"logScaleX": false,
|
||||||
"logScaleY": false,
|
"logScaleY": false,
|
||||||
"sensitivity": 1,
|
"sensitivity": 1,
|
||||||
"value": [0, 0],
|
"value": [
|
||||||
"default": [0, 0],
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"default": [
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
"linkId": "",
|
"linkId": "",
|
||||||
"address": "/VideoShaderToys/fisheye-reproject/xy",
|
"address": "/VideoShaderToys/fisheye-reproject/xy",
|
||||||
"preArgs": "",
|
"preArgs": "",
|
||||||
@@ -93,9 +98,135 @@
|
|||||||
"target": "127.0.0.1:9000",
|
"target": "127.0.0.1:9000",
|
||||||
"ignoreDefaults": false,
|
"ignoreDefaults": false,
|
||||||
"bypass": true,
|
"bypass": true,
|
||||||
"split": [],
|
|
||||||
"onCreate": "",
|
"onCreate": "",
|
||||||
"onValue": "if (touch !== undefined) return;\nvar pan = Array.isArray(value) ? Number(value[0]) : 0;\nvar tilt = Array.isArray(value) ? Number(value[1]) : 0;\nsend('127.0.0.1:9000', '/VideoShaderToys/fisheye-reproject/panDegrees', {type: 'f', value: pan});\nsend('127.0.0.1:9000', '/VideoShaderToys/fisheye-reproject/tiltDegrees', {type: 'f', value: tilt});",
|
"onValue": "var pan = Array.isArray(value) ? Number(value[0]) : 0;\nvar tilt = Array.isArray(value) ? Number(value[1]) : 0;\nsend('127.0.0.1:9000', '/VideoShaderToys/fisheye-reproject/panDegrees', {type: 'f', value: pan});\nsend('127.0.0.1:9000', '/VideoShaderToys/fisheye-reproject/tiltDegrees', {type: 'f', value: tilt});",
|
||||||
|
"onTouch": "",
|
||||||
|
"pointSize": 20,
|
||||||
|
"ephemeral": false,
|
||||||
|
"label": "",
|
||||||
|
"stepsX": false,
|
||||||
|
"stepsY": false,
|
||||||
|
"clipX": "",
|
||||||
|
"clipY": "",
|
||||||
|
"axisLock": "",
|
||||||
|
"doubleTap": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "fader",
|
||||||
|
"top": 120,
|
||||||
|
"left": 570,
|
||||||
|
"lock": false,
|
||||||
|
"id": "fader_1",
|
||||||
|
"visible": true,
|
||||||
|
"interaction": true,
|
||||||
|
"comments": "",
|
||||||
|
"width": 90,
|
||||||
|
"height": 420,
|
||||||
|
"expand": false,
|
||||||
|
"colorText": "auto",
|
||||||
|
"colorWidget": "auto",
|
||||||
|
"colorStroke": "auto",
|
||||||
|
"colorFill": "auto",
|
||||||
|
"alphaStroke": "auto",
|
||||||
|
"alphaFillOff": "auto",
|
||||||
|
"alphaFillOn": "auto",
|
||||||
|
"lineWidth": "auto",
|
||||||
|
"borderRadius": "auto",
|
||||||
|
"padding": "auto",
|
||||||
|
"html": "",
|
||||||
|
"css": "",
|
||||||
|
"design": "default",
|
||||||
|
"knobSize": "auto",
|
||||||
|
"colorKnob": "auto",
|
||||||
|
"horizontal": false,
|
||||||
|
"pips": false,
|
||||||
|
"dashed": false,
|
||||||
|
"gradient": [],
|
||||||
|
"snap": false,
|
||||||
|
"touchZone": "all",
|
||||||
|
"spring": false,
|
||||||
|
"doubleTap": false,
|
||||||
|
"range": {
|
||||||
|
"min": 100,
|
||||||
|
"max": 10
|
||||||
|
},
|
||||||
|
"logScale": false,
|
||||||
|
"sensitivity": 1,
|
||||||
|
"steps": "",
|
||||||
|
"origin": "auto",
|
||||||
|
"value": "",
|
||||||
|
"default": 90,
|
||||||
|
"linkId": "",
|
||||||
|
"address": "/VideoShaderToys/fisheye-reproject/virtualFovDegrees",
|
||||||
|
"preArgs": "",
|
||||||
|
"typeTags": "",
|
||||||
|
"decimals": 2,
|
||||||
|
"target": "127.0.0.1:9000",
|
||||||
|
"ignoreDefaults": false,
|
||||||
|
"bypass": false,
|
||||||
|
"onCreate": "",
|
||||||
|
"onValue": "",
|
||||||
|
"onTouch": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "xy",
|
||||||
|
"top": 700,
|
||||||
|
"left": 190,
|
||||||
|
"lock": false,
|
||||||
|
"id": "Pan Pad",
|
||||||
|
"visible": true,
|
||||||
|
"interaction": true,
|
||||||
|
"comments": "",
|
||||||
|
"width": "auto",
|
||||||
|
"height": "auto",
|
||||||
|
"expand": false,
|
||||||
|
"colorText": "auto",
|
||||||
|
"colorWidget": "auto",
|
||||||
|
"colorStroke": "auto",
|
||||||
|
"colorFill": "auto",
|
||||||
|
"alphaStroke": "auto",
|
||||||
|
"alphaFillOff": "auto",
|
||||||
|
"alphaFillOn": "auto",
|
||||||
|
"lineWidth": "auto",
|
||||||
|
"borderRadius": "auto",
|
||||||
|
"padding": "auto",
|
||||||
|
"html": "",
|
||||||
|
"css": "",
|
||||||
|
"pointSize": 20,
|
||||||
|
"ephemeral": false,
|
||||||
|
"pips": true,
|
||||||
|
"label": "",
|
||||||
|
"snap": false,
|
||||||
|
"spring": false,
|
||||||
|
"rangeX": {
|
||||||
|
"min": -1,
|
||||||
|
"max": 1
|
||||||
|
},
|
||||||
|
"rangeY": {
|
||||||
|
"min": -1,
|
||||||
|
"max": 1
|
||||||
|
},
|
||||||
|
"logScaleX": false,
|
||||||
|
"logScaleY": false,
|
||||||
|
"stepsX": false,
|
||||||
|
"stepsY": false,
|
||||||
|
"clipX": "",
|
||||||
|
"clipY": "",
|
||||||
|
"axisLock": "",
|
||||||
|
"doubleTap": false,
|
||||||
|
"sensitivity": 1,
|
||||||
|
"value": "",
|
||||||
|
"default": "",
|
||||||
|
"linkId": "",
|
||||||
|
"address": "/VideoShaderToys/video-transform/pan",
|
||||||
|
"preArgs": "",
|
||||||
|
"typeTags": "",
|
||||||
|
"decimals": 2,
|
||||||
|
"target": "",
|
||||||
|
"ignoreDefaults": false,
|
||||||
|
"bypass": true,
|
||||||
|
"onCreate": "",
|
||||||
|
"onValue": "var x = Array.isArray(value) ? Number(value[0]) : 0;\nvar y = Array.isArray(value) ? Number(value[1]) : 0;\nsend('127.0.0.1:9000', '/VideoShaderToys/video-transform/pan', {type: 'f', value: x}, {type: 'f', value: y});",
|
||||||
"onTouch": ""
|
"onTouch": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ std::vector<std::string> SplitAddress(const std::string& address)
|
|||||||
}
|
}
|
||||||
return parts;
|
return parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OscServer::OscServer()
|
OscServer::OscServer()
|
||||||
@@ -147,55 +148,35 @@ bool OscServer::DecodeMessage(const char* data, int byteCount, OscMessage& messa
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char valueType = typeTags[1];
|
std::vector<std::string> values;
|
||||||
if (valueType == 'f')
|
for (std::size_t index = 1; index < typeTags.size(); ++index)
|
||||||
{
|
{
|
||||||
double value = 0.0;
|
std::string valueJson;
|
||||||
if (!ReadFloat32(data, byteCount, offset, value))
|
if (!DecodeArgument(data, byteCount, offset, typeTags[index], valueJson))
|
||||||
|
{
|
||||||
|
error = "Unsupported or malformed OSC value type.";
|
||||||
return false;
|
return false;
|
||||||
std::ostringstream stream;
|
}
|
||||||
stream << std::setprecision(9) << value;
|
values.push_back(valueJson);
|
||||||
message.valueJson = stream.str();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (valueType == 'd')
|
if (values.size() == 1)
|
||||||
{
|
{
|
||||||
double value = 0.0;
|
message.valueJson = values.front();
|
||||||
if (!ReadFloat64(data, byteCount, offset, value))
|
|
||||||
return false;
|
|
||||||
std::ostringstream stream;
|
|
||||||
stream << std::setprecision(17) << value;
|
|
||||||
message.valueJson = stream.str();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (valueType == 'i')
|
std::ostringstream arrayJson;
|
||||||
|
arrayJson << "[";
|
||||||
|
for (std::size_t index = 0; index < values.size(); ++index)
|
||||||
{
|
{
|
||||||
int value = 0;
|
if (index > 0)
|
||||||
if (!ReadInt32(data, byteCount, offset, value))
|
arrayJson << ",";
|
||||||
return false;
|
arrayJson << values[index];
|
||||||
message.valueJson = std::to_string(value);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
arrayJson << "]";
|
||||||
if (valueType == 's')
|
message.valueJson = arrayJson.str();
|
||||||
{
|
return true;
|
||||||
std::string value;
|
|
||||||
if (!ReadPaddedString(data, byteCount, offset, value))
|
|
||||||
return false;
|
|
||||||
message.valueJson = BuildJsonString(value);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (valueType == 'T' || valueType == 'F')
|
|
||||||
{
|
|
||||||
message.valueJson = valueType == 'T' ? "true" : "false";
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
error = "Unsupported OSC value type.";
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OscServer::DispatchMessage(const OscMessage& message, std::string& error) const
|
bool OscServer::DispatchMessage(const OscMessage& message, std::string& error) const
|
||||||
@@ -211,6 +192,57 @@ bool OscServer::DispatchMessage(const OscMessage& message, std::string& error) c
|
|||||||
mCallbacks.updateParameter(parts[1], parts[2], message.valueJson, error);
|
mCallbacks.updateParameter(parts[1], parts[2], message.valueJson, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool OscServer::DecodeArgument(const char* data, int byteCount, int& offset, char valueType, std::string& valueJson)
|
||||||
|
{
|
||||||
|
if (valueType == 'f')
|
||||||
|
{
|
||||||
|
double value = 0.0;
|
||||||
|
if (!ReadFloat32(data, byteCount, offset, value))
|
||||||
|
return false;
|
||||||
|
std::ostringstream stream;
|
||||||
|
stream << std::setprecision(9) << value;
|
||||||
|
valueJson = stream.str();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valueType == 'd')
|
||||||
|
{
|
||||||
|
double value = 0.0;
|
||||||
|
if (!ReadFloat64(data, byteCount, offset, value))
|
||||||
|
return false;
|
||||||
|
std::ostringstream stream;
|
||||||
|
stream << std::setprecision(17) << value;
|
||||||
|
valueJson = stream.str();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valueType == 'i')
|
||||||
|
{
|
||||||
|
int value = 0;
|
||||||
|
if (!ReadInt32(data, byteCount, offset, value))
|
||||||
|
return false;
|
||||||
|
valueJson = std::to_string(value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valueType == 's')
|
||||||
|
{
|
||||||
|
std::string value;
|
||||||
|
if (!ReadPaddedString(data, byteCount, offset, value))
|
||||||
|
return false;
|
||||||
|
valueJson = BuildJsonString(value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valueType == 'T' || valueType == 'F')
|
||||||
|
{
|
||||||
|
valueJson = valueType == 'T' ? "true" : "false";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool OscServer::ReadPaddedString(const char* data, int byteCount, int& offset, std::string& value)
|
bool OscServer::ReadPaddedString(const char* data, int byteCount, int& offset, std::string& value)
|
||||||
{
|
{
|
||||||
if (offset < 0 || offset >= byteCount)
|
if (offset < 0 || offset >= byteCount)
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ private:
|
|||||||
void ServerLoop();
|
void ServerLoop();
|
||||||
bool DecodeMessage(const char* data, int byteCount, OscMessage& message, std::string& error) const;
|
bool DecodeMessage(const char* data, int byteCount, OscMessage& message, std::string& error) const;
|
||||||
bool DispatchMessage(const OscMessage& message, std::string& error) const;
|
bool DispatchMessage(const OscMessage& message, std::string& error) const;
|
||||||
|
static bool DecodeArgument(const char* data, int byteCount, int& offset, char valueType, std::string& valueJson);
|
||||||
static bool ReadPaddedString(const char* data, int byteCount, int& offset, std::string& value);
|
static bool ReadPaddedString(const char* data, int byteCount, int& offset, std::string& value);
|
||||||
static bool ReadInt32(const char* data, int byteCount, int& offset, int& value);
|
static bool ReadInt32(const char* data, int byteCount, int& offset, int& value);
|
||||||
static bool ReadFloat32(const char* data, int byteCount, int& offset, double& value);
|
static bool ReadFloat32(const char* data, int byteCount, int& offset, double& value);
|
||||||
|
|||||||
63
shaders/composition-guides/shader.json
Normal file
63
shaders/composition-guides/shader.json
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"id": "composition-guides",
|
||||||
|
"name": "Composition Guides",
|
||||||
|
"description": "Overlays rule-of-thirds guides and a center crosshair for camera alignment and framing.",
|
||||||
|
"category": "Utility",
|
||||||
|
"entryPoint": "shadeVideo",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"id": "showThirds",
|
||||||
|
"label": "Rule of Thirds",
|
||||||
|
"type": "bool",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "showCrosshair",
|
||||||
|
"label": "Center Crosshair",
|
||||||
|
"type": "bool",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "lineColor",
|
||||||
|
"label": "Line Color",
|
||||||
|
"type": "color",
|
||||||
|
"default": [1.0, 1.0, 1.0, 1.0]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "lineOpacity",
|
||||||
|
"label": "Line Opacity",
|
||||||
|
"type": "float",
|
||||||
|
"default": 0.65,
|
||||||
|
"min": 0.0,
|
||||||
|
"max": 1.0,
|
||||||
|
"step": 0.01
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "lineThicknessPixels",
|
||||||
|
"label": "Line Thickness",
|
||||||
|
"type": "float",
|
||||||
|
"default": 2.0,
|
||||||
|
"min": 0.5,
|
||||||
|
"max": 12.0,
|
||||||
|
"step": 0.1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "crosshairSizePixels",
|
||||||
|
"label": "Crosshair Size",
|
||||||
|
"type": "float",
|
||||||
|
"default": 54.0,
|
||||||
|
"min": 8.0,
|
||||||
|
"max": 240.0,
|
||||||
|
"step": 1.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "crosshairGapPixels",
|
||||||
|
"label": "Crosshair Gap",
|
||||||
|
"type": "float",
|
||||||
|
"default": 10.0,
|
||||||
|
"min": 0.0,
|
||||||
|
"max": 80.0,
|
||||||
|
"step": 1.0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
48
shaders/composition-guides/shader.slang
Normal file
48
shaders/composition-guides/shader.slang
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
float lineMask(float coordinate, float target, float pixelThickness, float resolution)
|
||||||
|
{
|
||||||
|
float halfWidth = max(pixelThickness, 0.5) * 0.5 / max(resolution, 1.0);
|
||||||
|
float distanceToLine = abs(coordinate - target);
|
||||||
|
float feather = halfWidth * 0.85 + (1.0 / max(resolution, 1.0));
|
||||||
|
return 1.0 - smoothstep(halfWidth, feather, distanceToLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
float thirdsMask(float2 uv, float2 resolution)
|
||||||
|
{
|
||||||
|
float thickness = max(lineThicknessPixels, 0.5);
|
||||||
|
float mask = 0.0;
|
||||||
|
mask = max(mask, lineMask(uv.x, 1.0 / 3.0, thickness, resolution.x));
|
||||||
|
mask = max(mask, lineMask(uv.x, 2.0 / 3.0, thickness, resolution.x));
|
||||||
|
mask = max(mask, lineMask(uv.y, 1.0 / 3.0, thickness, resolution.y));
|
||||||
|
mask = max(mask, lineMask(uv.y, 2.0 / 3.0, thickness, resolution.y));
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
float crosshairMask(float2 uv, float2 resolution)
|
||||||
|
{
|
||||||
|
float2 pixel = (uv - 0.5) * resolution;
|
||||||
|
float halfThickness = max(lineThicknessPixels, 0.5) * 0.5;
|
||||||
|
float halfSize = max(crosshairSizePixels, 1.0) * 0.5;
|
||||||
|
float halfGap = max(crosshairGapPixels, 0.0) * 0.5;
|
||||||
|
|
||||||
|
float horizontalExtent = step(abs(pixel.x), halfSize) * (1.0 - step(abs(pixel.x), halfGap));
|
||||||
|
float verticalExtent = step(abs(pixel.y), halfSize) * (1.0 - step(abs(pixel.y), halfGap));
|
||||||
|
|
||||||
|
float horizontalLine = horizontalExtent * (1.0 - smoothstep(halfThickness, halfThickness + 1.5, abs(pixel.y)));
|
||||||
|
float verticalLine = verticalExtent * (1.0 - smoothstep(halfThickness, halfThickness + 1.5, abs(pixel.x)));
|
||||||
|
return max(horizontalLine, verticalLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 shadeVideo(ShaderContext context)
|
||||||
|
{
|
||||||
|
float2 resolution = max(context.outputResolution, float2(1.0, 1.0));
|
||||||
|
float mask = 0.0;
|
||||||
|
|
||||||
|
if (showThirds)
|
||||||
|
mask = max(mask, thirdsMask(context.uv, resolution));
|
||||||
|
if (showCrosshair)
|
||||||
|
mask = max(mask, crosshairMask(context.uv, resolution));
|
||||||
|
|
||||||
|
float opacity = saturate(lineOpacity * lineColor.a) * mask;
|
||||||
|
float3 color = lerp(context.sourceColor.rgb, lineColor.rgb, opacity);
|
||||||
|
return float4(color, context.sourceColor.a);
|
||||||
|
}
|
||||||
54
shaders/video-transform/shader.json
Normal file
54
shaders/video-transform/shader.json
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
{
|
||||||
|
"id": "video-transform",
|
||||||
|
"name": "Video Transform",
|
||||||
|
"description": "Zooms, pans, and rotates the video by remapping output pixels back into source UV space.",
|
||||||
|
"category": "Utility",
|
||||||
|
"entryPoint": "shadeVideo",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"id": "zoom",
|
||||||
|
"label": "Zoom",
|
||||||
|
"type": "float",
|
||||||
|
"default": 1.0,
|
||||||
|
"min": 0.1,
|
||||||
|
"max": 8.0,
|
||||||
|
"step": 0.01
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "pan",
|
||||||
|
"label": "Pan",
|
||||||
|
"type": "vec2",
|
||||||
|
"default": [0.0, 0.0],
|
||||||
|
"min": [-1.0, -1.0],
|
||||||
|
"max": [1.0, 1.0],
|
||||||
|
"step": [0.001, 0.001]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "rotationDegrees",
|
||||||
|
"label": "Rotation",
|
||||||
|
"type": "float",
|
||||||
|
"default": 0.0,
|
||||||
|
"min": -180.0,
|
||||||
|
"max": 180.0,
|
||||||
|
"step": 0.1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "edgeMode",
|
||||||
|
"label": "Edge Mode",
|
||||||
|
"type": "enum",
|
||||||
|
"default": "black",
|
||||||
|
"options": [
|
||||||
|
{ "value": "black", "label": "Black" },
|
||||||
|
{ "value": "clamp", "label": "Clamp" },
|
||||||
|
{ "value": "wrap", "label": "Wrap" },
|
||||||
|
{ "value": "mirror", "label": "Mirror" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "outsideColor",
|
||||||
|
"label": "Outside Color",
|
||||||
|
"type": "color",
|
||||||
|
"default": [0.0, 0.0, 0.0, 1.0]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
44
shaders/video-transform/shader.slang
Normal file
44
shaders/video-transform/shader.slang
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
static const float PI = 3.14159265358979323846;
|
||||||
|
|
||||||
|
float2 rotateAroundCenter(float2 uv, float radians)
|
||||||
|
{
|
||||||
|
float2 centered = uv - 0.5;
|
||||||
|
float s = sin(radians);
|
||||||
|
float c = cos(radians);
|
||||||
|
return float2(c * centered.x - s * centered.y, s * centered.x + c * centered.y) + 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
float mirroredCoordinate(float coordinate)
|
||||||
|
{
|
||||||
|
float wrapped = frac(coordinate * 0.5) * 2.0;
|
||||||
|
return wrapped <= 1.0 ? wrapped : 2.0 - wrapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
float2 applyEdgeMode(float2 uv, out bool inside)
|
||||||
|
{
|
||||||
|
inside = uv.x >= 0.0 && uv.x <= 1.0 && uv.y >= 0.0 && uv.y <= 1.0;
|
||||||
|
|
||||||
|
if (edgeMode == 1)
|
||||||
|
return clamp(uv, 0.0, 1.0);
|
||||||
|
if (edgeMode == 2)
|
||||||
|
return frac(uv);
|
||||||
|
if (edgeMode == 3)
|
||||||
|
return float2(mirroredCoordinate(uv.x), mirroredCoordinate(uv.y));
|
||||||
|
|
||||||
|
return uv;
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 shadeVideo(ShaderContext context)
|
||||||
|
{
|
||||||
|
float safeZoom = max(zoom, 0.001);
|
||||||
|
float2 sourceUv = (context.uv - 0.5) / safeZoom + 0.5;
|
||||||
|
sourceUv -= pan;
|
||||||
|
sourceUv = rotateAroundCenter(sourceUv, -rotationDegrees * (PI / 180.0));
|
||||||
|
|
||||||
|
bool inside = false;
|
||||||
|
float2 sampledUv = applyEdgeMode(sourceUv, inside);
|
||||||
|
if (!inside && edgeMode == 0)
|
||||||
|
return outsideColor;
|
||||||
|
|
||||||
|
return sampleVideo(sampledUv);
|
||||||
|
}
|
||||||
@@ -110,6 +110,20 @@ void TestDecodeDoubleMessage()
|
|||||||
Expect(message.valueJson.find("51.5") == 0, "double OSC value becomes JSON number");
|
Expect(message.valueJson.find("51.5") == 0, "double OSC value becomes JSON number");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestDecodeVectorMessage()
|
||||||
|
{
|
||||||
|
OscServer server;
|
||||||
|
std::vector<char> packet = BuildOscPacket("/VideoShaderToys/video-transform/pan", ",ff");
|
||||||
|
AppendFloat32(packet, 0.25f);
|
||||||
|
AppendFloat32(packet, -0.5f);
|
||||||
|
|
||||||
|
OscServerTestAccess::Message message;
|
||||||
|
std::string error;
|
||||||
|
Expect(OscServerTestAccess::Decode(server, packet, message, error), "multi-float OSC message decodes");
|
||||||
|
Expect(message.address == "/VideoShaderToys/video-transform/pan", "multi-float OSC address is preserved");
|
||||||
|
Expect(message.valueJson.find("[0.25,-0.5") == 0, "multi-float OSC value becomes JSON array");
|
||||||
|
}
|
||||||
|
|
||||||
void TestDecodeIntStringAndBoolMessages()
|
void TestDecodeIntStringAndBoolMessages()
|
||||||
{
|
{
|
||||||
OscServer server;
|
OscServer server;
|
||||||
@@ -183,6 +197,7 @@ int main()
|
|||||||
{
|
{
|
||||||
TestDecodeFloatMessage();
|
TestDecodeFloatMessage();
|
||||||
TestDecodeDoubleMessage();
|
TestDecodeDoubleMessage();
|
||||||
|
TestDecodeVectorMessage();
|
||||||
TestDecodeIntStringAndBoolMessages();
|
TestDecodeIntStringAndBoolMessages();
|
||||||
TestDispatchValidAddress();
|
TestDispatchValidAddress();
|
||||||
TestRejectsUnsupportedAddress();
|
TestRejectsUnsupportedAddress();
|
||||||
|
|||||||
Reference in New Issue
Block a user