1479 lines
48 KiB
Mathematica
1479 lines
48 KiB
Mathematica
(* ::Package:: *)
|
|
|
|
(* ::Title:: *)
|
|
(*Render*)
|
|
|
|
|
|
(* ::Subtitle:: *)
|
|
(*Various rendering techniques on grids.*)
|
|
|
|
|
|
(* ::Text:: *)
|
|
(*Copyright Contributors to the OpenVDB Project*)
|
|
(*SPDX-License-Identifier: Apache-2.0*)
|
|
|
|
|
|
(* ::Section:: *)
|
|
(*Initialization & Usage*)
|
|
|
|
|
|
Package["OpenVDBLink`"]
|
|
|
|
|
|
PackageExport["OpenVDBLevelSetRender"]
|
|
PackageExport["OpenVDBLevelSetViewer"]
|
|
|
|
|
|
OpenVDBLevelSetRender::usage = "OpenVDBLevelSetRender[expr] returns a 3D render Image of an OpenVDB level set grid.";
|
|
|
|
|
|
OpenVDBLevelSetViewer::usage = "OpenVDBLevelSetViewer[expr] returns a manipulatable 3D render Image of an OpenVDB level set grid.";
|
|
|
|
|
|
(* ::Section:: *)
|
|
(*Shader and theme names*)
|
|
|
|
|
|
(* ::Subsection::Closed:: *)
|
|
(*Shaders*)
|
|
|
|
|
|
$dielectricList = {"Rubber", "Plastic", "Diffuse", "Glazed", "Satin", "Clay", "Matte", "Foil"};
|
|
$metalList = {"Gold", "Aluminum", "Brass", "Bronze", "Copper", "Electrum", "Iron", "Pewter"};
|
|
$rainbowList = {"Normal", "Position"};
|
|
$depthList = {"Depth"};
|
|
|
|
|
|
(* ::Subsection::Closed:: *)
|
|
(*Themes*)
|
|
|
|
|
|
$renderColorThemes = <|
|
|
(* front face back face closed face background *)
|
|
"Default" -> {RGBColor["#4D94FF"], RGBColor["#FFA24E"], RGBColor["#4D94FF"], RGBColor[1, 1, 1]},
|
|
"Monochrome" -> {RGBColor[1, 1, 1], RGBColor[1, 1, 1], RGBColor[1, 1, 1], RGBColor[0, 0, 0]},
|
|
"Bold" -> {ColorData[106, 2], ColorData[106, 1], ColorData[106, 2], Lighter[ColorData[106, 4], 0.5]},
|
|
"Cool" -> {ColorData[107, 1], ColorData[107, 4], ColorData[107, 1], RGBColor["#E6EBFF"]},
|
|
"Neon" -> {ColorData[109, 8], ColorData[109, 2], ColorData[109, 8], ColorData[109, 3]},
|
|
"Pastel" -> {RGBColor["#D5B0F6"], RGBColor["#F5AE6F"], RGBColor["#D5B0F6"], RGBColor["#B9FFDB"]},
|
|
"Soft" -> {RGBColor["#CFCFE1"], RGBColor["#EBBBAC"], RGBColor["#CFCFE1"], RGBColor["#3C3C78"]},
|
|
"Vibrant" -> {ColorData[112, 2], ColorData[112, 1], ColorData[112, 2], RGBColor["#FFDC80"]},
|
|
"Warm" -> {ColorData[113, 1], ColorData[113, 2], ColorData[113, 1], RGBColor["#FFE7B7"]}
|
|
|>;
|
|
|
|
|
|
(* ::Section:: *)
|
|
(*OpenVDBLevelSetRender*)
|
|
|
|
|
|
(* ::Subsection::Closed:: *)
|
|
(*Main*)
|
|
|
|
|
|
Options[OpenVDBLevelSetRender] = Join[
|
|
{Background -> Automatic, "ClosedClipping" -> False, "FrameTranslation" -> Automatic, ImageResolution -> Automatic,
|
|
"IsoValue" -> 0.0, "OrthographicFrame" -> Automatic, PerformanceGoal :> $PerformanceGoal},
|
|
Options[Graphics3D, {ImageSize, ViewAngle, ViewCenter, ViewPoint, ViewProjection, ViewRange, ViewVertical}]
|
|
];
|
|
|
|
|
|
OpenVDBLevelSetRender[args___] /; !CheckArgs[OpenVDBLevelSetRender[args], {1, 2}] = $Failed;
|
|
|
|
|
|
OpenVDBLevelSetRender[args___] :=
|
|
With[{res = iLevelSetRender[args]},
|
|
res /; res =!= $Failed
|
|
]
|
|
|
|
|
|
OpenVDBLevelSetRender[args___] := mLevelSetRender[args]
|
|
|
|
|
|
(* ::Subsection::Closed:: *)
|
|
(*iLevelSetRender*)
|
|
|
|
|
|
Options[iLevelSetRender] = Options[OpenVDBLevelSetRender];
|
|
|
|
|
|
iLevelSetRender[vdb_?OpenVDBScalarGridQ, shading_, opts:OptionsPattern[]] /; levelSetQ[vdb] :=
|
|
Block[{ropts, res},
|
|
ropts = parseRenderOptions[vdb, shading, opts];
|
|
(
|
|
res = oLevelSetRender[vdb, ropts];
|
|
|
|
res /; res =!= $Failed
|
|
|
|
) /; AssociationQ[ropts]
|
|
]
|
|
|
|
|
|
iLevelSetRender[vdb_, opts:OptionsPattern[]] := iLevelSetRender[vdb, Automatic, opts]
|
|
|
|
|
|
iLevelSetRender[___] = $Failed;
|
|
|
|
|
|
(* ::Subsection::Closed:: *)
|
|
(*Argument conform & completion*)
|
|
|
|
|
|
registerForLevelSet[iLevelSetRender, 1];
|
|
|
|
|
|
SyntaxInformation[OpenVDBLevelSetRender] = {"ArgumentsPattern" -> {_, _., OptionsPattern[]}};
|
|
|
|
|
|
addCodeCompletion[OpenVDBLevelSetRender][None, Join[Keys[$renderColorThemes], $dielectricList, $metalList], None];
|
|
|
|
|
|
(* ::Subsection::Closed:: *)
|
|
(*oLevelSetRender*)
|
|
|
|
|
|
Options[oLevelSetRender] = Options[iLevelSetRender];
|
|
|
|
|
|
oLevelSetRender[vdb_, ropts_] /; emptyVDBQ[vdb] || (!fogVolume[vdb] && vdb["BackgroundValue"] <= ropts["IsoValue"]) :=
|
|
Block[{bg, res, ires},
|
|
bg = RGBColor @@ ropts["Background"];
|
|
res = ropts["Resolution"];
|
|
ires = ropts["ImageResolution"];
|
|
|
|
ConstantImage[bg, res, "Byte", ImageResolution -> ires]
|
|
]
|
|
|
|
|
|
oLevelSetRender[vdb_, ropts_] /; KeyExistsQ[materialParameters, ropts["Shader"]] :=
|
|
Block[{ires, pbrparams, args, im},
|
|
ires = ropts["ImageResolution"];
|
|
args = Lookup[ropts, $PBRrenderLevelSetArgumentKeys];
|
|
pbrparams = materialParameters[ropts["Shader"]];
|
|
(
|
|
im = vdb["renderGridPBR"[##]]& @@ Join[args, Values[pbrparams][[4 ;; -1]]];
|
|
|
|
Image[im, ImageResolution -> ires] /; ImageQ[im]
|
|
) /; AssociationQ[pbrparams]
|
|
]
|
|
|
|
|
|
oLevelSetRender[vdb_, ropts_] :=
|
|
Block[{ires, args, im},
|
|
ires = ropts["ImageResolution"];
|
|
args = Lookup[ropts, $renderLevelSetArgumentKeys];
|
|
im = vdb["renderGrid"[##]]& @@ args;
|
|
|
|
Image[im, ImageResolution -> ires] /; ImageQ[im]
|
|
]
|
|
|
|
|
|
oLevelSetRender[___] = $Failed;
|
|
|
|
|
|
(* ::Subsection::Closed:: *)
|
|
(*Messages*)
|
|
|
|
|
|
mLevelSetRender[args___] := messageRenderFunction[OpenVDBLevelSetRender, args]
|
|
|
|
|
|
(* ::Section:: *)
|
|
(*OpenVDBLevelSetViewer*)
|
|
|
|
|
|
(* ::Subsection::Closed:: *)
|
|
(*Main*)
|
|
|
|
|
|
Options[OpenVDBLevelSetViewer] = Options[OpenVDBLevelSetRender];
|
|
|
|
|
|
OpenVDBLevelSetViewer[args___] /; !CheckArgs[OpenVDBLevelSetViewer[args], {1, 2}] = $Failed;
|
|
|
|
|
|
OpenVDBLevelSetViewer[args___] :=
|
|
With[{res = iLevelSetViewer[args]},
|
|
res /; res =!= $Failed
|
|
]
|
|
|
|
|
|
OpenVDBLevelSetViewer[args___] := mLevelSetViewer[args]
|
|
|
|
|
|
(* ::Subsection::Closed:: *)
|
|
(*iLevelSetViewer*)
|
|
|
|
|
|
Options[iLevelSetViewer] = Options[OpenVDBLevelSetViewer];
|
|
|
|
|
|
iLevelSetViewer[vdb_?OpenVDBScalarGridQ, shading_, opts:OptionsPattern[]] /; levelSetQ[vdb] :=
|
|
Block[{ropts, args, im},
|
|
ropts = parseRenderOptions[vdb, shading, opts];
|
|
(
|
|
iDynamicRender[vdb, shading, ropts, opts]
|
|
|
|
) /; AssociationQ[ropts]
|
|
]
|
|
|
|
|
|
iLevelSetViewer[vdb_, opts:OptionsPattern[]] := iLevelSetViewer[vdb, Automatic, opts]
|
|
|
|
|
|
iLevelSetViewer[___] = $Failed;
|
|
|
|
|
|
(* ::Subsection::Closed:: *)
|
|
(*Argument conform & completion*)
|
|
|
|
|
|
registerForLevelSet[iLevelSetViewer, 1];
|
|
|
|
|
|
SyntaxInformation[OpenVDBLevelSetViewer] = {"ArgumentsPattern" -> {_, _., OptionsPattern[]}};
|
|
|
|
|
|
addCodeCompletion[OpenVDBLevelSetViewer][None, Join[Keys[$renderColorThemes], $dielectricList, $metalList], None];
|
|
|
|
|
|
(* ::Subsection::Closed:: *)
|
|
(*iDynamicRender*)
|
|
|
|
|
|
Options[iDynamicRender] = Options[OpenVDBLevelSetViewer];
|
|
|
|
|
|
iDynamicRender[vdb_, shading_, iropts_, opts:OptionsPattern[]] :=
|
|
DynamicModule[{t, l, b, vp, ovp, vv, ovv, va, ova, vc, vr, sz, dx = 0.0, dy = 0.0, dz = 0.0, origva, origvc, origvr, origvp, origvv, mem, iso, oiso, mniso, mxiso, ir,
|
|
td\[Delta], \[Delta], shader, oshader, vrng, ovrng, volQ, ivolQ, projection, oprojection, origprojection, oframe, ooframe, c1, oc1, c2, oc2, c3, oc3, bg, obg, clp, im, antialq, oantialq},
|
|
{t, l, b} = Lookup[iropts, {"Translate", "LookAt", "Bounds"}];
|
|
td\[Delta] = {0.005, 0.005, 0.1}*Max[Abs[Subtract @@@ b]];
|
|
|
|
{vp, vv} = OptionValue[{ViewPoint, ViewVertical}];
|
|
va = constructViewAngle[OptionValue[ViewAngle], t, l, b];
|
|
vc = parseRenderViewCenter[OptionValue[ViewCenter], t, l, {{0, 1}, {0, 1}, {0, 1}}];
|
|
|
|
origvc = vc;
|
|
origvp = ovp = vp;
|
|
origvv = ovv = vv;
|
|
origva = ova = va;
|
|
|
|
origvr = ovrng = initialViewRange[OptionValue[ViewRange], vp, vc, vv, va, b];
|
|
volQ = ivolQ = TrueQ[OptionValue["ClosedClipping"]];
|
|
|
|
ir = OptionValue[ImageResolution];
|
|
sz = initialImageSize[iropts["ImageSize"], OptionValue[ImageSize]];
|
|
oshader = canonicalizeShader[shading][[1]];
|
|
|
|
projection = Replace[OptionValue[ViewProjection], Automatic -> "Perspective", {0}];
|
|
origprojection = oprojection = projection;
|
|
oframe = orthographicSphericalFrame[b];
|
|
ooframe = oframe;
|
|
|
|
{c1, c2, c3, bg} = If[TrueQ[customColorQ[oshader]],
|
|
RGBColor @@@ Lookup[iropts, {"BaseColorFront", "BaseColorBack", "BaseColorClosed", "Background"}],
|
|
$renderColorThemes["Default"]
|
|
];
|
|
{oc1, oc2, oc3, obg} = {c1, c2, c3, bg};
|
|
|
|
oiso = iropts["IsoValue"];
|
|
{mniso, mxiso} = {-1, 1}Ramp[vdb["BackgroundValue"] - 1.5voxelSize[vdb]];
|
|
mem = Quotient[vdb["MemoryUsage"], Replace[$ProcessorCount, Except[_Integer?Positive] -> 1, {0}]];
|
|
|
|
oantialq = OptionValue[PerformanceGoal] =!= "Speed";
|
|
|
|
Manipulate[
|
|
Which[
|
|
dz != 0,
|
|
vp = zoomViewPoint[vp, vc, td\[Delta][[3]]dz, vv, va, b],
|
|
dx != 0 || dy != 0,
|
|
vc = translateViewCenter[vc, {td\[Delta][[1]]dx, td\[Delta][[2]]dy}]
|
|
];
|
|
Overlay[
|
|
{
|
|
Dynamic @ Image[im = iRender[
|
|
vdb, makeShader[shader, c1, c2, c3], "BoundingBox" -> b, ViewPoint -> vp, ViewVertical -> vv, ViewAngle -> va, ViewCenter -> vc,
|
|
ViewRange -> Scaled[vrng], ViewProjection -> projection, "OrthographicFrame" -> oframe, Background -> bg,
|
|
ImageSize -> dRenderImageSize[sz, \[Delta], OptionValue[PerformanceGoal], mem], "IsoValue" -> Clip[iso, .999{mniso, mxiso}],
|
|
PerformanceGoal -> If[antialq && !$ControlActiveSetting, "Quality", "Speed"], ImageResolution -> ir,
|
|
"ClosedClipping" -> volQ, opts
|
|
], ImageSize -> sz],
|
|
Graphics3D[{},
|
|
Boxed -> False, Method -> {}, ImageSize -> Dynamic[sz],
|
|
ViewPoint -> Dynamic[vp], ViewVertical -> Dynamic[vv], ViewAngle -> Dynamic[va], ViewCenter -> Dynamic[vc]
|
|
]
|
|
},
|
|
{1, 2},
|
|
2
|
|
],
|
|
OpenerView[{
|
|
Style["Appearance", Medium],
|
|
Column[{
|
|
Control[{{shader, oshader, "shading"},
|
|
KeyValueMap[#1 -> Row[{If[#2 === Automatic, Dynamic[c1], #2], ToLowerCase[#1]}, Spacer[2]]&, $dynamicRenderShaders],
|
|
ControlType -> PopupMenu
|
|
}],
|
|
Row[{
|
|
Control[{{antialq, oantialq, "anti aliasing"}, {True, False}}],
|
|
Control[{{volQ, ivolQ, "closed clipping"}, {True, False}}]
|
|
}, Spacer[2]],
|
|
Control[{{\[Delta], 1, "resolution"}, 0, 1}],
|
|
If[mxiso > 0, Control[{{iso, oiso, "iso\[Hyphen]value"}, mniso, mxiso}], Nothing],
|
|
Row[{
|
|
"themes",
|
|
Row[KeyValueMap[themeButton[ToLowerCase[#1], {c1, c2, c3, bg} = #2, Enabled -> Dynamic[customColorQ[shader]]]&, $renderColorThemes]]
|
|
}, Spacer[2]],
|
|
Dynamic @ Which[
|
|
!customColorQ[shader],
|
|
Row[{
|
|
Control[{{bg, obg, "background"}, obg, ControlType -> ColorSetter}]
|
|
}, Spacer[10]],
|
|
!volQ,
|
|
Row[{
|
|
Control[{{bg, obg, "background"}, obg, ControlType -> ColorSetter}],
|
|
Control[{{c1, oc1, "front face"}, oc1, ControlType -> ColorSetter}],
|
|
Control[{{c2, oc2, "back face"}, oc2, ControlType -> ColorSetter}], Spacer[3],
|
|
If[c1 =!= c2, themeButton["flip faces", If[c1 === c3, c3 = c2]; {c1, c2} = {c2, c1}], Nothing]
|
|
}, Spacer[1]],
|
|
c1 === c2 === c3,
|
|
Row[{
|
|
Control[{{bg, obg, "background"}, obg, ControlType -> ColorSetter}],
|
|
Control[{{c1, oc1, "front face"}, oc1, ControlType -> ColorSetter}],
|
|
Control[{{c2, oc2, "back face"}, oc2, ControlType -> ColorSetter}],
|
|
Control[{{c3, oc3, "closed face"}, oc3, ControlType -> ColorSetter}]
|
|
}, Spacer[1]],
|
|
True,
|
|
Grid[{{
|
|
Control[{{c1, oc1, "front face"}, oc1, ControlType -> ColorSetter}],
|
|
Control[{{c2, oc2, "back face"}, oc2, ControlType -> ColorSetter}], Spacer[3],
|
|
If[c1 =!= c2, Row[{themeButton["flip faces", If[c1 === c3, c3 = c2]; {c1, c2} = {c2, c1}], ""}], Nothing]
|
|
}, {
|
|
Control[{{bg, obg, "background"}, obg, ControlType -> ColorSetter}],
|
|
Control[{{c3, oc3, "closed face"}, oc3, ControlType -> ColorSetter}], Spacer[3],
|
|
If[c1 =!= c3, Row[{themeButton["set closed as front", c3 = c1], ""}], Nothing]
|
|
}},
|
|
Alignment -> {{Right, Right, Left, Left}, Automatic},
|
|
Spacings -> {1, Automatic}]
|
|
]
|
|
},
|
|
Spacings -> {Automatic, {Automatic, Automatic, Automatic, 1, 1}}]
|
|
}, Method -> "Active"],
|
|
OpenerView[{
|
|
Style["Orientation", Medium],
|
|
Column[{
|
|
Control[{{vv, ovv, "view vertical"}, {{0,0,1} -> "(0,0,1)", {0,0,-1} -> "(0,0,\[Hyphen]1)", {1,0,0} -> "(1,0,0)", {-1,0,0} -> "(\[Hyphen]1,0,0)", {0,1,0} -> "(0,1,0)", {0,-1,0} -> "(0,\[Hyphen]1,0)"}, ControlType -> Setter}],
|
|
Control[{{vp, ovp, "view point"}, {{1.3,-2.4,2} -> "default", {-3,0,0} -> "left", {3,0,0} -> "right", {0,-3,0} -> "front", {0,3,0} -> "back", {0,0,-3} -> "below", {0,0,3} -> "above"}, ControlType -> Setter}],
|
|
Row[{
|
|
"view point\[ThinSpace]&\[ThinSpace]center",
|
|
Framed[
|
|
Grid[{{
|
|
EventHandler[panSlider[Dynamic[dz, (dz = If[Abs[#] < .05, 0., #])&], Enabled -> Dynamic[projection =!= "Orthographic"]], {"MouseUp" :> (dz = 0)}, PassEventsDown -> True],
|
|
EventHandler[panSlider[Dynamic[dx, (dx = If[Abs[#] < .05, 0., #])&]], {"MouseUp" :> (dx = 0)}, PassEventsDown -> True],
|
|
EventHandler[panSlider[Dynamic[dy, (dy = If[Abs[#] < .05, 0., #])&]], {"MouseUp" :> (dy = 0)}, PassEventsDown -> True]
|
|
},
|
|
{"out\[ThinSpace]\[TwoWayRule]\[ThinSpace]in", "left\[ThinSpace]\[TwoWayRule]\[ThinSpace]right", "down\[ThinSpace]\[TwoWayRule]\[ThinSpace]up"}},
|
|
Spacings -> {1, 0.5}
|
|
],
|
|
ContentPadding -> True,
|
|
FrameStyle -> None,
|
|
ImageMargins -> {{0,0},{2,0}}
|
|
]
|
|
}, Spacer[2]]
|
|
}]
|
|
}, Method -> "Active"],
|
|
OpenerView[{
|
|
Style["Field of View", Medium],
|
|
Column[{
|
|
Row[{
|
|
Control[{{vrng, ovrng, "clipping"}, 0, 1, ControlType -> IntervalSlider, MinIntervalSize -> 0.01, Method -> "Stop"}],
|
|
Control[{{volQ, ivolQ, "closed"}, {True, False}}]
|
|
}, Spacer[3]],
|
|
Dynamic @ If[projection =!= "Orthographic",
|
|
Control[{{va, ova, "FOV (rad)"}, 0, 75*\[Pi]/180}],
|
|
Control[{{oframe, ooframe, "frame width"}, 0, ooframe}]
|
|
],
|
|
Control[{{projection, oprojection, "projection"}, {"Perspective" -> "perspective", "Orthographic" -> "orthographic"}}]
|
|
}]
|
|
}, Method -> "Active"],
|
|
OpenerView[{
|
|
Style["General", Medium],
|
|
Row[{
|
|
Button["Copy image",
|
|
CopyToClipboard[im]],
|
|
Button["Copy view settings",
|
|
CopyToClipboard[copyString @ {If[projection =!= "Orthographic", ViewAngle -> va, "OrthographicFrame" -> oframe], ViewCenter -> vc,
|
|
ViewRange -> unscaledViewRange[vrng, vp, vc, vv, va, b], ViewPoint -> vp, ViewProjection -> projection, ViewVertical -> vv}]],
|
|
Button["Reset view settings",
|
|
va = origva; vc = origvc; vrng = origvr; vp = origvp; vv = origvv; projection = origprojection; oframe = ooframe;]
|
|
}]
|
|
}, Method -> "Active"]
|
|
],
|
|
Initialization -> With[{args = Sequence @@ Append[vdb, $SessionID]}, iDynamicRenderTrack[args] = vdb],
|
|
Deinitialization :> With[{args = Sequence @@ Append[vdb, $SessionID]}, If[Head[iDynamicRenderTrack[args]] =!= iDynamicRenderTrack, iDynamicRenderTrack[args]=.]]
|
|
]
|
|
|
|
|
|
(* ::Subsection::Closed:: *)
|
|
(*iRender*)
|
|
|
|
|
|
Options[iRender] = Options[iLevelSetRender];
|
|
|
|
|
|
iRender[args__] :=
|
|
With[{res = Quiet[iLevelSetRender[args]]},
|
|
res /; ImageQ[res]
|
|
]
|
|
|
|
|
|
iRender[___] = Image[{{1}}];
|
|
|
|
|
|
(* ::Subsection::Closed:: *)
|
|
(*Utilities*)
|
|
|
|
|
|
Scan[(customColorQ[#] = True )&, $dielectricList];
|
|
Scan[(customColorQ[#] = False)&, $metalList];
|
|
Scan[(customColorQ[#] = False)&, $rainbowList];
|
|
Scan[(customColorQ[#] = True)&, $depthList];
|
|
|
|
|
|
$dynamicRenderShaders := $dynamicRenderShaders = Association @ Join[
|
|
# -> Automatic& /@ $dielectricList,
|
|
# -> materialParameters[#]["BaseColorFront"]& /@ $metalList,
|
|
# -> $rainbowSwatch& /@ $rainbowList,
|
|
# -> $depthSwatch& /@ $depthList
|
|
]
|
|
|
|
|
|
dynamicRenderShaders = $dynamicRenderShaders[If[ListQ[#], First[#, ""], #]]&;
|
|
|
|
|
|
$rainbowSwatch = Image[ImagePad[Image[Table[With[{r = Sqrt[x^2 + y^2], \[Theta] = ArcTan[.0000001-y, x]}, Hue[\[Theta]/(2\[Pi])+1/2., Clip[r,{0,1}],1]], {x, -1., 1., .1}, {y, -1., 1., .1}]], 1], "Byte", ImageSize -> 11];
|
|
|
|
|
|
$depthSwatch = Image[ImagePad[Image[Table[With[{r = Sqrt[x^2 + y^2], \[Theta] = ArcTan[.0000001-y, x]}, Hue[0, 0, Clip[1-r, {0,1}]^0.5]], {x, -1., 1., .1}, {y, -1., 1., .1}]], 1], "Byte", ImageSize -> 11];
|
|
|
|
|
|
makeShader[shader_, colors__] :=
|
|
If[TrueQ[customColorQ[shader]] || !KeyExistsQ[materialParameters, shader],
|
|
{shader, colors},
|
|
Prepend[Lookup[materialParameters[shader], {"BaseColorFront", "BaseColorBack", "BaseColorClosed"}], shader]
|
|
]
|
|
|
|
|
|
initialViewRange[{min_?NumericQ, max_?NumericQ}, vp_, vc_, vv_, va_, bds_] /; min <= max :=
|
|
Block[{t, l, vr},
|
|
t = parseRenderViewPoint[vp, bds];
|
|
l = parseRenderViewCenter[vc, t, vv, va, bds];
|
|
vr = parseRenderViewRange[Scaled[{0, 1}], t, l, bds];
|
|
Clip[Rescale[{min, max}, vr], {0, 1}]
|
|
]
|
|
initialViewRange[___] = {0, 1};
|
|
|
|
|
|
unscaledViewRange[vrng_, vp_, vc_, vv_, va_, bds_] :=
|
|
Block[{t, l},
|
|
t = parseRenderViewPoint[vp, bds];
|
|
l = parseRenderViewCenter[vc, t, vv, va, bds];
|
|
parseRenderViewRange[Scaled[vrng], t, l, bds]
|
|
]
|
|
|
|
|
|
copyString[expr_List] := StringTake[ToString[expr, InputForm], {2, -2}]
|
|
|
|
|
|
copyString[expr_] := ToString[expr, InputForm]
|
|
|
|
|
|
initialImageSize[{sx_, _}, Automatic] := {sx, sx}
|
|
initialImageSize[sz_, _] := sz
|
|
|
|
|
|
dRenderImageSize[sz_, \[Delta]_, pgoal_, mem_] :=
|
|
With[{s = sz*\[Delta]},
|
|
Clip[Round[dPGoalFactor[pgoal, s, mem]*s], {1, \[Infinity]}]
|
|
]
|
|
|
|
|
|
(* ::Text:: *)
|
|
(*TODO figure out when rotating becomes laggy. I think it's some function of image size, vdb memory footprint, and average depth of the first layer.*)
|
|
|
|
|
|
dPGoalFactor["Speed", sz_, mem_] :=
|
|
If[TrueQ[(Times @@ sz) > Min[450^2, Divide[500000000.*720^2, mem]] && $ControlActiveSetting],
|
|
0.5,
|
|
1.0
|
|
]
|
|
dPGoalFactor[__] = 1.0;
|
|
|
|
|
|
panSlider[var_, opts___] := Slider[var, {-1, 1}, opts, Appearance -> "UpArrow", ImageSize -> {75, 22}]
|
|
|
|
|
|
orthographicSphericalFrame[bds_] :=
|
|
With[{reg = BoundingRegion[Tuples[bds], "MinBall"]},
|
|
2reg[[2]] /; MatchQ[reg, Ball[_, _Real]]
|
|
]
|
|
|
|
|
|
orthographicSphericalFrame[___] = $Failed;
|
|
|
|
|
|
renderColor[__, "Normal"|"NormalClosed"|"Position"|"PositionClosed"] = White;
|
|
renderColor[c1_, c1_, _] := c1
|
|
renderColor[c1_, c2_, _] := FaceForm[c1, c2]
|
|
|
|
|
|
SetAttributes[themeButton, HoldRest];
|
|
themeButton[label_, action_, opts___] := Button[Style[label, Small], action, opts, Appearance -> "Palette"]
|
|
|
|
|
|
translateViewCenter[{vc_, {x_, y_}}, {dx_, dy_}] := {vc, {x+dx, y+dy}}
|
|
translateViewCenter[vc_, {dx_, dy_}] := {vc, {0.5+dx, 0.5+dy}}
|
|
|
|
|
|
zoomViewPoint[vp_, vc_, dz_, vv_, va_, bds_] :=
|
|
Block[{translate, lookat, dir, center, mx},
|
|
translate = parseRenderViewPoint[vp, bds];
|
|
lookat = parseRenderViewCenter[vc, translate, vv, va, bds];
|
|
dir = Min[.1dz*Power[Norm[Subtract[lookat, translate]], 1.25], 1.5]Normalize[Subtract[lookat, translate]];
|
|
|
|
translate += dir;
|
|
lookat += dir;
|
|
|
|
center = Mean /@ bds;
|
|
mx = Replace[Max[Abs[Subtract @@@ bds]], _?NonPositive -> 1.0, {0}];
|
|
|
|
Divide[Subtract[translate, center], mx]
|
|
]
|
|
|
|
|
|
(* ::Subsection::Closed:: *)
|
|
(*Messages*)
|
|
|
|
|
|
mLevelSetViewer[args___] := messageRenderFunction[OpenVDBLevelSetViewer, args]
|
|
|
|
|
|
(* ::Section:: *)
|
|
(*Utilities*)
|
|
|
|
|
|
(* ::Subsection::Closed:: *)
|
|
(*$renderLevelSetArgumentKeys*)
|
|
|
|
|
|
$renderLevelSetArgumentKeys = {"IsoValue", "BaseColorFront", "BaseColorBack", "BaseColorClosed", "Background", "Translate", "LookAt", "Up",
|
|
"Range", "FOV", "Shader", "Camera", "Samples", "Resolution", "Frame", "DepthParameters", "Lighting", "Step", "IsClosed"};
|
|
|
|
|
|
$PBRrenderLevelSetArgumentKeys = {"IsoValue", "Background", "Translate", "LookAt", "Up",
|
|
"Range", "FOV", "Camera", "Samples", "Resolution", "Frame", "IsClosed", "BaseColorFront", "BaseColorBack", "BaseColorClosed"};
|
|
|
|
|
|
(* ::Subsection::Closed:: *)
|
|
(*Option parsing utilities*)
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*parseRenderOptions*)
|
|
|
|
|
|
Options[parseRenderOptions] = Join[Options[iLevelSetRender], {"BoundingBox" -> Automatic}];
|
|
|
|
|
|
parseRenderOptions[vdb_, shading_, opts:OptionsPattern[]] :=
|
|
Block[{res},
|
|
res = iparseRenderOptions[vdb, shading, opts];
|
|
|
|
res /; AssociationQ[res] && NoneTrue[res, FailureQ]
|
|
]
|
|
|
|
|
|
parseRenderOptions[___] = $Failed;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*iparseRenderOptions*)
|
|
|
|
|
|
Options[iparseRenderOptions] = Options[parseRenderOptions];
|
|
|
|
|
|
iparseRenderOptions[vdb_, shading_, OptionsPattern[]] :=
|
|
Block[{bds, translate, lookat1, \[Alpha], lookat, up, transdist, depthdata, imgresolution, shader, ropts},
|
|
bds = parseBoundingBox[vdb, OptionValue["BoundingBox"]];
|
|
|
|
translate = parseRenderViewPoint[OptionValue[ViewPoint], bds];
|
|
up = parseRenderViewVertical[OptionValue[ViewVertical], OptionValue[ViewPoint]];
|
|
|
|
lookat1 = parseRenderViewCenterNoOffset[OptionValue[ViewCenter], bds];
|
|
\[Alpha] = constructViewAngle[OptionValue[ViewAngle], translate, lookat1, bds];
|
|
lookat = parseRenderViewCenter[OptionValue[ViewCenter], translate, up, \[Alpha], bds];
|
|
|
|
shader = canonicalizeShader[shading];
|
|
|
|
depthdata = parseDepthParameters[shader];
|
|
imgresolution = parseRenderImageResolution[OptionValue[ImageResolution]];
|
|
|
|
<|
|
|
"Bounds" -> bds,
|
|
"IsoValue" -> parseIsoValue[OptionValue["IsoValue"]],
|
|
"Background" -> parseRenderBackgroundColor[OptionValue[Background], shader],
|
|
"Translate" -> translate,
|
|
"LookAt" -> lookat,
|
|
"Up" -> up,
|
|
"Range" -> parseRenderViewRange[OptionValue[ViewRange], translate, lookat, bds],
|
|
"FOV" -> parseRenderViewAngle[OptionValue[ViewAngle], translate, lookat, bds],
|
|
"Shader" -> parseRenderShader[First[shader, $Failed]],
|
|
"Camera" -> parseRenderViewProjection[OptionValue[ViewProjection], OptionValue[ViewPoint]],
|
|
"Samples" -> parseRenderPerformance[OptionValue[PerformanceGoal]],
|
|
"Resolution" -> parseRenderImageSize[OptionValue[ImageSize], imgresolution, translate, up, bds],
|
|
"ImageSize" -> parseRenderUnscaledImageSize[OptionValue[ImageSize], translate, up, bds],
|
|
"ImageResolution" -> imgresolution,
|
|
"Frame" -> parseOrthographicFrame[OptionValue[ViewProjection], OptionValue["OrthographicFrame"], translate, lookat, up, bds],
|
|
"DepthParameters" -> depthdata,
|
|
"Lighting" -> RotationTransform[0.25, {-1, 1, 1}][translate - lookat],
|
|
"Step" -> {1.0, 2.0},
|
|
"BaseColorFront" -> parseRenderColor[shader],
|
|
"BaseColorBack" -> parseRenderColor2[shader],
|
|
"BaseColorClosed" -> parseRenderColor3[shader],
|
|
"IsClosed" -> TrueQ[OptionValue["ClosedClipping"]]
|
|
|>
|
|
]
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*renderFailureOption*)
|
|
|
|
|
|
renderFailureOption[assoc_] :=
|
|
(
|
|
If[FailureQ[assoc["Bounds"]], Return["BoundingBox"]];
|
|
If[FailureQ[assoc["IsoValue"]], Return["IsoValue"]];
|
|
|
|
If[FailureQ[assoc["Shader"]], Return["Shader"]];
|
|
If[FailureQ[assoc["Background"]], Return[Background]];
|
|
|
|
If[FailureQ[assoc["Translate"]], Return[ViewPoint]];
|
|
If[FailureQ[assoc["Up"]], Return[ViewVertical]];
|
|
If[FailureQ[assoc["FOV"]], Return[ViewAngle]];
|
|
If[FailureQ[assoc["LookAt"]], Return[ViewCenter]];
|
|
If[FailureQ[assoc["Range"]], Return[ViewRange]];
|
|
|
|
If[FailureQ[assoc["Camera"]], Return[ViewProjection]];
|
|
If[FailureQ[assoc["Samples"]], Return[PerformanceGoal]];
|
|
If[FailureQ[assoc["ImageResolution"]], Return[ImageResolution]];
|
|
If[FailureQ[assoc["ImageSize"]], Return[ImageSize]];
|
|
|
|
$Failed
|
|
)
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*canonicalizeShader*)
|
|
|
|
|
|
$defaultShaderSpec = "Rubber";
|
|
$defaultColorSpec = "Default";
|
|
|
|
|
|
canonicalizeShader[shader_String] /; KeyExistsQ[materialParameters, shader] := Prepend[Lookup[materialParameters[shader], {"BaseColorFront", "BaseColorBack", "BaseColorClosed"}], shader]
|
|
|
|
|
|
canonicalizeShader[theme_String] /; KeyExistsQ[$renderColorThemes, theme] := {$defaultShaderSpec, theme}
|
|
|
|
|
|
canonicalizeShader[shader:("Depth"|{"Depth", _?NumericQ, __})] := {shader, RGBColor[1, 1, 1], RGBColor[1, 1, 1], RGBColor[1, 1, 1]}
|
|
|
|
|
|
canonicalizeShader[{shader_, theme_String}] := {shader, theme}
|
|
|
|
|
|
canonicalizeShader[{shader_, icolor_?ColorQ}] :=
|
|
With[{color = ColorConvert[icolor, "RGB"][[1 ;; 3]]},
|
|
{shader, color, color, color}
|
|
]
|
|
|
|
|
|
canonicalizeShader[{shader_, icolorf_?ColorQ, icolorb_?ColorQ}] :=
|
|
With[{
|
|
colorf = ColorConvert[icolorf, "RGB"][[1 ;; 3]],
|
|
colorb = ColorConvert[icolorb, "RGB"][[1 ;; 3]]
|
|
},
|
|
{shader, colorf, colorb, colorf}
|
|
]
|
|
|
|
|
|
canonicalizeShader[{shader_, colorf_?ColorQ, colorb_?ColorQ, colorc_?ColorQ}] :=
|
|
{shader, ColorConvert[colorf, "RGB"][[1 ;; 3]], ColorConvert[colorb, "RGB"][[1 ;; 3]], ColorConvert[colorc, "RGB"][[1 ;; 3]]}
|
|
|
|
|
|
canonicalizeShader[Automatic] = {$defaultShaderSpec, $defaultColorSpec};
|
|
|
|
|
|
canonicalizeShader[{colors__?ColorQ}] := canonicalizeShader[{$defaultShaderSpec, colors}]
|
|
|
|
|
|
canonicalizeShader[color_?ColorQ] := canonicalizeShader[{$defaultShaderSpec, color}]
|
|
|
|
|
|
canonicalizeShader[shader_] := {shader, $defaultColorSpec}
|
|
|
|
|
|
canonicalizeShader[___] = $Failed;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*parseBoundingBox*)
|
|
|
|
|
|
parseBoundingBox[vdb_, bds_] := If[bds === Automatic, vdb["WorldBoundingBox"], bds]
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*parseIsoValue*)
|
|
|
|
|
|
parseIsoValue[Automatic] = 0.0;
|
|
parseIsoValue[x_?realQ] := x
|
|
parseIsoValue[___] = $Failed;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*parseRenderColor*)
|
|
|
|
|
|
parseRenderColor[{"Normal"|"NormalClosed"|"Position"|"PositionClosed", __}] = {1.0, 1.0, 1.0};
|
|
parseRenderColor[{_, theme_String}] :=
|
|
With[{color = $renderColorThemes[theme][[1]]},
|
|
List @@ color /; ColorQ[color]
|
|
]
|
|
parseRenderColor[{_, color_?ColorQ, _, _}] := List @@ color
|
|
parseRenderColor[___] = $Failed;
|
|
|
|
|
|
parseRenderColor2[{"Normal"|"NormalClosed"|"Position"|"PositionClosed", __}] = {1.0, 1.0, 1.0};
|
|
parseRenderColor2[{_, theme_String}] :=
|
|
With[{color = $renderColorThemes[theme][[2]]},
|
|
List @@ color /; ColorQ[color]
|
|
]
|
|
parseRenderColor2[{_, _, color_?ColorQ, _}] := List @@ color
|
|
parseRenderColor2[___] = $Failed;
|
|
|
|
|
|
parseRenderColor3[{"Normal"|"NormalClosed"|"Position"|"PositionClosed", __}] = {1.0, 1.0, 1.0};
|
|
parseRenderColor3[{_, theme_String}] :=
|
|
With[{color = $renderColorThemes[theme][[3]]},
|
|
List @@ color /; ColorQ[color]
|
|
]
|
|
parseRenderColor3[{_, _, _, color_?ColorQ}] := List @@ color
|
|
parseRenderColor3[___] = $Failed;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*parseRenderBackgroundColor*)
|
|
|
|
|
|
parseRenderBackgroundColor[None, shader_] := parseRenderBackgroundColor[White, shader]
|
|
parseRenderBackgroundColor[Automatic, shader:{"Depth"|{"Depth", ___}|"DepthClosed"|{"DepthClosed", ___}, __}] := parseRenderBackgroundColor[Black, shader]
|
|
parseRenderBackgroundColor[Automatic, shader_] := parseRenderBackgroundColor[White, shader]
|
|
parseRenderBackgroundColor[Automatic, {shader_, t_String}] /; KeyExistsQ[$renderColorThemes, t] := parseRenderBackgroundColor[$renderColorThemes[t][[4]], {shader, t}]
|
|
parseRenderBackgroundColor[color_?ColorQ, __] := List @@ ColorConvert[color, "RGB"][[1 ;; 3]]
|
|
parseRenderBackgroundColor[___] = $Failed;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*parseRenderViewPoint*)
|
|
|
|
|
|
canonicalizeViewPoint[Left] = {-2,0,0};
|
|
canonicalizeViewPoint[Right] = { 2,0,0};
|
|
canonicalizeViewPoint[Front] = {0,-2,0};
|
|
canonicalizeViewPoint[Back] = {0, 2,0};
|
|
canonicalizeViewPoint[Below] = {0,0,-2};
|
|
canonicalizeViewPoint[Above] = {0,0, 2};
|
|
canonicalizeViewPoint[vp_] := vp
|
|
|
|
|
|
parseRenderViewPoint[vp_List, bds_?bounds3DQ] /; VectorQ[vp, NumericQ] && Length[vp] === 3 :=
|
|
Block[{vpfinite, mx},
|
|
vpfinite = vp /. inf_DirectedInfinity :> Sign[inf]*1000;
|
|
mx = Replace[Max[Abs[Subtract @@@ bds]], _?NonPositive -> 1.0, {0}];
|
|
|
|
mx * vpfinite + (Mean /@ bds)
|
|
]
|
|
|
|
|
|
parseRenderViewPoint[dir:(Left|Right|Front|Back|Below|Above), bds_] := parseRenderViewPoint[canonicalizeViewPoint[dir], bds]
|
|
|
|
|
|
parseRenderViewPoint[dirs:{(Left|Right|Front|Back|Below|Above)..}, bds_] :=
|
|
Block[{canons, vps},
|
|
canons = canonicalizeViewPoint /@ dirs;
|
|
(
|
|
vps = GatherBy[canons, Unitize][[All, -1]];
|
|
|
|
Total[parseRenderViewPoint[canonicalizeViewPoint[#], bds]& /@ vps]
|
|
|
|
) /; MatrixQ[canons]
|
|
]
|
|
|
|
|
|
parseRenderViewPoint[___] = $Failed;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*parseRenderViewCenter*)
|
|
|
|
|
|
parseRenderViewCenter[{vc_List, {ox_?NumericQ, oy_?NumericQ}}, translate_, up_, \[Alpha]_, bds_?bounds3DQ] :=
|
|
Block[{lookat = parseRenderViewCenterNoOffset[vc, bds], v, cross, rot},
|
|
(
|
|
v = translate-lookat;
|
|
cross = Cross[v, up];
|
|
|
|
rot = RotationTransform[(oy-0.5)*\[Alpha], cross, translate] @* RotationTransform[(ox-0.5)*\[Alpha], up, translate];
|
|
|
|
rot[lookat]
|
|
|
|
) /; lookat =!= $Failed
|
|
]
|
|
parseRenderViewCenter[vc_, __, bds_] := parseRenderViewCenterNoOffset[vc, bds];
|
|
|
|
|
|
parseRenderViewCenterNoOffset[Automatic, bds_] := parseRenderViewCenterNoOffset[{0.5, 0.5, 0.5}, bds]
|
|
parseRenderViewCenterNoOffset[vc_List, bds_?bounds3DQ] /; VectorQ[vc, NumericQ] && Length[vc] === 3 := MapThread[Dot, {Transpose[{1-vc, vc}], bds}]
|
|
parseRenderViewCenterNoOffset[{vc_List, _}, bds_?bounds3DQ] := parseRenderViewCenterNoOffset[vc, bds]
|
|
parseRenderViewCenterNoOffset[___] = $Failed;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*parseRenderViewVertical*)
|
|
|
|
|
|
parseRenderViewVertical[vv_List, vp_] /; VectorQ[vp, NumericQ] && Length[vp] === 3 :=
|
|
If[degenerateViewVerticalQ[vv, vp],
|
|
fixDegenerateViewVertical[vp],
|
|
vv
|
|
]
|
|
parseRenderViewVertical[___] = $Failed;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*parseRenderViewRange*)
|
|
|
|
|
|
parseRenderViewRange[Automatic|All, vp_, vc_, bds_] :=
|
|
Block[{corners, inview, far},
|
|
corners = Tuples[bds];
|
|
inview = Pick[corners, RegionMember[HalfSpace[vp-vc, vp], corners]];
|
|
If[Length[inview] == 0,
|
|
parseRenderViewRange[{0.0, 0.0}, vp, vc, bds],
|
|
far = Sqrt[Max[Total[(Transpose[inview] - vp)^2]]];
|
|
parseRenderViewRange[{0.0, far}, vp, vc, bds]
|
|
]
|
|
]
|
|
|
|
|
|
parseRenderViewRange[{min_?NumericQ, max_?NumericQ}, ___] /; min <= max :=
|
|
Block[{clip},
|
|
clip = Clip[{min, max}, {.001, \[Infinity]}];
|
|
If[Equal @@ clip,
|
|
clip + {0, 0.001},
|
|
clip
|
|
]
|
|
]
|
|
|
|
|
|
parseRenderViewRange[Scaled[vrng:{min_?NumericQ, max_?NumericQ}], vp_, vc_, bds_] /; min <= max :=
|
|
Block[{padding, vrmin, vrmax},
|
|
padding = 0.02*Max[Abs[Subtract @@@ bds]];
|
|
vrmin = Clip[SignedRegionDistance[Cuboid @@ Transpose[bds], vp] - padding, {0.1, \[Infinity]}];
|
|
vrmax = Ramp[Sqrt[Max[Total[Subtract[Transpose[Tuples[bds]], vp]^2, {1}]]] + .11];
|
|
|
|
vrng*(vrmax-vrmin) + vrmin
|
|
]
|
|
|
|
|
|
parseRenderViewRange[___] = $Failed;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*parseRenderViewAngle*)
|
|
|
|
|
|
$renderFocalLength = 50.0;
|
|
|
|
|
|
parseRenderViewAngle[args__] :=
|
|
With[{aperture = viewAngleAperture[args]},
|
|
{aperture, $renderFocalLength} /; aperture =!= $Failed && NumericQ[$renderFocalLength]
|
|
]
|
|
parseRenderViewAngle[___] = $Failed;
|
|
|
|
|
|
viewAngleAperture[args__] :=
|
|
With[{\[Alpha] = constructViewAngle[args]},
|
|
2$renderFocalLength*Tan[Clip[\[Alpha], {.001, 3.14059}]/2] /; \[Alpha] =!= $Failed
|
|
];
|
|
viewAngleAperture[___] = $Failed;
|
|
|
|
|
|
constructViewAngle[All, translate_, lookat_, bds_] :=
|
|
With[{v = lookat - translate},
|
|
2.0*Max[VectorAngle[v, # - translate]& /@ Tuples[bds]]
|
|
];
|
|
constructViewAngle[Automatic, args__] :=
|
|
With[{\[Alpha] = constructViewAngle[All, args]},
|
|
Min[35.0*Degree, \[Alpha]] /; \[Alpha] =!= $Failed
|
|
];
|
|
constructViewAngle[\[Alpha]_?NumericQ, args__] /; 0 <= \[Alpha] <= \[Pi] := \[Alpha]
|
|
constructViewAngle[___] = $Failed;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*parseRenderShader*)
|
|
|
|
|
|
parseRenderShader["Diffuse"] = 0;
|
|
|
|
|
|
parseRenderShader["Matte"] = 1;
|
|
|
|
|
|
parseRenderShader["Normal"] = 2;
|
|
|
|
|
|
parseRenderShader["Position"] = 3;
|
|
|
|
|
|
parseRenderShader["Depth"] = 4;
|
|
parseRenderShader[{"Depth", ___}] = parseRenderShader["Depth"];
|
|
|
|
|
|
parseRenderShader[shader_String] /; KeyExistsQ[materialParameters, shader] := shader
|
|
|
|
|
|
parseRenderShader[___] = $Failed;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*parseRenderPerformance*)
|
|
|
|
|
|
parseRenderPerformance["Speed"] = 1;
|
|
parseRenderPerformance["Quality"] = 9;
|
|
parseRenderPerformance[___] = $Failed;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*parseDepthParameters*)
|
|
|
|
|
|
$imin = 0.0;
|
|
$imax = 1.0;
|
|
$gamma = 1.0;
|
|
|
|
|
|
parseDepthParameters[{{"Depth"|"DepthClosed", args___}, __}] := iParseDepthParameters[args]
|
|
parseDepthParameters[___] = {$imin, $imax, $gamma};
|
|
|
|
|
|
iParseDepthParameters[] = {$imin, $imax, $gamma};
|
|
iParseDepthParameters[gamma_?Positive] := {$imin, $imax, gamma}
|
|
iParseDepthParameters[gamma_?Positive, {imin_, imax_}] /; 0 <= imin <= imax <= 1 := {imin, imax, gamma}
|
|
iParseDepthParameters[___] = $Failed;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*parseRenderViewProjection*)
|
|
|
|
|
|
parseRenderViewProjection["Perspective", _] = 0;
|
|
parseRenderViewProjection["Orthographic", _] = 1;
|
|
parseRenderViewProjection[Automatic, vp_] :=
|
|
With[{p = If[FreeQ[vp, _DirectedInfinity], "Perspective", "Orthographic"]},
|
|
parseRenderViewProjection[p, vp]
|
|
]
|
|
parseRenderViewProjection[___] = $Failed;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*parseRenderImageResolution*)
|
|
|
|
|
|
parseRenderImageResolution[Automatic] := imageResolution[]
|
|
parseRenderImageResolution[x_?Positive] := x
|
|
parseRenderImageResolution[___] = $Failed;
|
|
|
|
|
|
imageResolution[] := Replace[Max[Quiet[CurrentValue[{"ConnectedDisplays", "Resolution"}]]], Except[_Real|_Integer] -> 72., {0}]
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*parseRenderImageSize*)
|
|
|
|
|
|
parseRenderImageSize[x:(_Integer|Automatic), args__] := parseRenderImageSize[{x, Automatic}, args]
|
|
parseRenderImageSize[{x_Integer?Positive, y_Integer?Positive}, res_, __] := res/72*{x, y}
|
|
parseRenderImageSize[{x_, y_}, res_, vp_List, vv_List, bds_?bounds3DQ] :=
|
|
Block[{aspectratio, w, h},
|
|
aspectratio = 1.0;(*viewPointAspectRatio[bds, vp, vv];*)
|
|
w = Round[Replace[x, Automatic -> 360*res/72, {0}]];
|
|
h = Round[Replace[y, Automatic -> w*aspectratio, {0}]];
|
|
|
|
{w, h}
|
|
]
|
|
parseRenderImageSize[___] = $Failed
|
|
|
|
|
|
viewPointAspectRatio[bds_, vp_, vv_] :=
|
|
Block[{rot, proj1, up2d, proj2},
|
|
rot = RotationTransform[{vp-(Mean /@ bds), {0,0,1}}];
|
|
proj1 = rot[Tuples[bds]][[All, 1;;2]];
|
|
up2d = rot[vv][[1 ;; 2]];
|
|
|
|
proj2 = RotationTransform[{up2d, {0, 1}}][proj1];
|
|
Divide @@ Clip[Reverse[Abs[Subtract @@@ CoordinateBounds[proj2]]], {0.001, \[Infinity]}]
|
|
]
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*parseRenderUnscaledImageSize*)
|
|
|
|
|
|
parseRenderUnscaledImageSize[x:(_Integer|Automatic), args__] := parseRenderUnscaledImageSize[{x, Automatic}, args]
|
|
parseRenderUnscaledImageSize[{x_Integer?Positive, y_Integer?Positive}, __] := {x, y}
|
|
parseRenderUnscaledImageSize[{x_, y_}, vp_List, vv_List, bds_?bounds3DQ] :=
|
|
Block[{aspectratio, w, h},
|
|
aspectratio = viewPointAspectRatio[bds, vp, vv];
|
|
w = Round[Replace[x, Automatic -> 360, {0}]];
|
|
h = Round[Replace[y, Automatic -> w*aspectratio, {0}]];
|
|
|
|
{w, h}
|
|
]
|
|
parseRenderUnscaledImageSize[___] = $Failed
|
|
|
|
|
|
viewPointAspectRatio[___] = 1.0;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*parseOrthographicFrame*)
|
|
|
|
|
|
parseOrthographicFrame["Orthographic", x_?NumericQ, __] := Clip[x, {0.001, \[Infinity]}]
|
|
parseOrthographicFrame["Orthographic", Automatic, vp_, vc_, vv_, bds_] :=
|
|
Block[{rot, proj1, up2d, proj2},
|
|
rot = RotationTransform[{vp-vc, {0,0,1}}];
|
|
proj1 = rot[Tuples[bds]][[All, 1;;2]];
|
|
up2d = rot[vv][[1 ;; 2]];
|
|
|
|
proj2 = RotationTransform[{up2d, {0, 1}}][proj1];
|
|
Clip[Abs[Subtract @@@ CoordinateBounds[proj2]][[1]], {0.001, \[Infinity]}]
|
|
]
|
|
parseOrthographicFrame["Perspective"|Automatic, __] = 1.0;
|
|
parseOrthographicFrame[___] = $Failed;
|
|
|
|
|
|
(* ::Subsection::Closed:: *)
|
|
(*Degenerate ViewVertical*)
|
|
|
|
|
|
degenerateViewVerticalQ[vv_, vp_] := Chop[Norm[Cross[vv, canonicalizeViewPoint[vp]]]] === 0
|
|
|
|
|
|
fixDegenerateViewVertical[vp_] := Normalize[RotateLeft[canonicalizeViewPoint[vp]]]
|
|
|
|
|
|
(* ::Subsection::Closed:: *)
|
|
(*MaterialShading parameters*)
|
|
|
|
|
|
(* ::Text:: *)
|
|
(*Found through ctrl-shift-e on MaterialShading[] outputs.*)
|
|
|
|
|
|
materialParameters = <||>;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*Aluminum*)
|
|
|
|
|
|
materialParameters["Aluminum"] = <|
|
|
"BaseColorFront" -> RGBColor[0.95, 0.95, 0.95],
|
|
"BaseColorBack" -> RGBColor[0.95, 0.95, 0.95],
|
|
"BaseColorClosed" -> RGBColor[0.95, 0.95, 0.95],
|
|
"MetallicCoefficient" -> 0.8,
|
|
"RoughnessCoefficient" -> 0.75,
|
|
"SpecularAnisotropyCoefficient" -> 0.6,
|
|
"Reflectance" -> 0.5,
|
|
"CoatColor" -> {1.0, 1.0, 1.0},
|
|
"CoatRoughnessCoefficient" -> 0.0,
|
|
"CoatAnisotropyCoefficient" -> 0.0,
|
|
"CoatReflectance" -> 0.5,
|
|
"SpecularColorMultiplier" -> 1.0,
|
|
"DiffuseColorMultiplier" -> 1.0,
|
|
"CoatColorMultiplier" -> 0.0
|
|
|>;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*Brass*)
|
|
|
|
|
|
materialParameters["Brass"] = <|
|
|
"BaseColorFront" -> RGBColor[0.9, 0.855, 0.45],
|
|
"BaseColorBack" -> RGBColor[0.9, 0.855, 0.45],
|
|
"BaseColorClosed" -> RGBColor[0.9, 0.855, 0.45],
|
|
"MetallicCoefficient" -> 0.8,
|
|
"RoughnessCoefficient" -> 0.65,
|
|
"SpecularAnisotropyCoefficient" -> 0.4,
|
|
"Reflectance" -> 0.5,
|
|
"CoatColor" -> {1.0, 1.0, 1.0},
|
|
"CoatRoughnessCoefficient" -> 0.0,
|
|
"CoatAnisotropyCoefficient" -> 0.0,
|
|
"CoatReflectance" -> 0.5,
|
|
"SpecularColorMultiplier" -> 1,
|
|
"DiffuseColorMultiplier" -> 1.0,
|
|
"CoatColorMultiplier" -> 0.0
|
|
|>;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*Bronze*)
|
|
|
|
|
|
materialParameters["Bronze"] = <|
|
|
"BaseColorFront" -> RGBColor[0.9, 0.68625, 0.45],
|
|
"BaseColorBack" -> RGBColor[0.9, 0.68625, 0.45],
|
|
"BaseColorClosed" -> RGBColor[0.9, 0.68625, 0.45],
|
|
"MetallicCoefficient" -> 0.8,
|
|
"RoughnessCoefficient" -> 0.65,
|
|
"SpecularAnisotropyCoefficient" -> 0.3,
|
|
"Reflectance" -> 0.5,
|
|
"CoatColor" -> {1.0, 1.0, 1.0},
|
|
"CoatRoughnessCoefficient" -> 0.0,
|
|
"CoatAnisotropyCoefficient" -> 0.0,
|
|
"CoatReflectance" -> 0.5,
|
|
"SpecularColorMultiplier" -> 1,
|
|
"DiffuseColorMultiplier" -> 1.0,
|
|
"CoatColorMultiplier" -> 0.0
|
|
|>;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*Copper*)
|
|
|
|
|
|
materialParameters["Copper"] = <|
|
|
"BaseColorFront" -> RGBColor[1.0, 0.65, 0.5],
|
|
"BaseColorBack" -> RGBColor[1.0, 0.65, 0.5],
|
|
"BaseColorClosed" -> RGBColor[1.0, 0.65, 0.5],
|
|
"MetallicCoefficient" -> 0.8,
|
|
"RoughnessCoefficient" -> 0.65,
|
|
"SpecularAnisotropyCoefficient" -> 0.3,
|
|
"Reflectance" -> 0.5,
|
|
"CoatColor" -> {1.0, 1.0, 1.0},
|
|
"CoatRoughnessCoefficient" -> 0.0,
|
|
"CoatAnisotropyCoefficient" -> 0.0,
|
|
"CoatReflectance" -> 0.5,
|
|
"SpecularColorMultiplier" -> 1,
|
|
"DiffuseColorMultiplier" -> 1.0,
|
|
"CoatColorMultiplier" -> 0.0
|
|
|>;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*Electrum*)
|
|
|
|
|
|
materialParameters["Electrum"] = <|
|
|
"BaseColorFront" -> RGBColor[0.9, 0.774, 0.45],
|
|
"BaseColorBack" -> RGBColor[0.9, 0.774, 0.45],
|
|
"BaseColorClosed" -> RGBColor[0.9, 0.774, 0.45],
|
|
"MetallicCoefficient" -> 0.7,
|
|
"RoughnessCoefficient" -> 0.7,
|
|
"SpecularAnisotropyCoefficient" -> 0.3,
|
|
"Reflectance" -> 0.5,
|
|
"CoatColor" -> {1.0, 1.0, 1.0},
|
|
"CoatRoughnessCoefficient" -> 0.0,
|
|
"CoatAnisotropyCoefficient" -> 0.0,
|
|
"CoatReflectance" -> 0.5,
|
|
"SpecularColorMultiplier" -> 1,
|
|
"DiffuseColorMultiplier" -> 1.0,
|
|
"CoatColorMultiplier" -> 0.0
|
|
|>;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*Gold*)
|
|
|
|
|
|
materialParameters["Gold"] = <|
|
|
"BaseColorFront" -> RGBColor[1.0, 0.75, 0.0],
|
|
"BaseColorBack" -> RGBColor[1.0, 0.75, 0.0],
|
|
"BaseColorClosed" -> RGBColor[1.0, 0.75, 0.0],
|
|
"MetallicCoefficient" -> 0.8,
|
|
"RoughnessCoefficient" -> 0.65,
|
|
"SpecularAnisotropyCoefficient" -> 0.3,
|
|
"Reflectance" -> 0.5,
|
|
"CoatColor" -> {1.0, 1.0, 1.0},
|
|
"CoatRoughnessCoefficient" -> 0.0,
|
|
"CoatAnisotropyCoefficient" -> 0.0,
|
|
"CoatReflectance" -> 0.5,
|
|
"SpecularColorMultiplier" -> 1.0,
|
|
"DiffuseColorMultiplier" -> 1.0,
|
|
"CoatColorMultiplier" -> 0.0
|
|
|>;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*Iron*)
|
|
|
|
|
|
materialParameters["Iron"] = <|
|
|
"BaseColorFront" -> RGBColor[0.6, 0.576, 0.54],
|
|
"BaseColorBack" -> RGBColor[0.6, 0.576, 0.54],
|
|
"BaseColorClosed" -> RGBColor[0.6, 0.576, 0.54],
|
|
"MetallicCoefficient" -> 0.7,
|
|
"RoughnessCoefficient" -> 0.6,
|
|
"SpecularAnisotropyCoefficient" -> 0.3,
|
|
"Reflectance" -> 0.5,
|
|
"CoatColor" -> {1.0, 1.0, 1.0},
|
|
"CoatRoughnessCoefficient" -> 0.0,
|
|
"CoatAnisotropyCoefficient" -> 0.0,
|
|
"CoatReflectance" -> 0.5,
|
|
"SpecularColorMultiplier" -> 1,
|
|
"DiffuseColorMultiplier" -> 1.0,
|
|
"CoatColorMultiplier" -> 0.0
|
|
|>;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*Pewter*)
|
|
|
|
|
|
materialParameters["Pewter"] = <|
|
|
"BaseColorFront" -> RGBColor[0.9, 0.864, 0.81],
|
|
"BaseColorBack" -> RGBColor[0.9, 0.864, 0.81],
|
|
"BaseColorClosed" -> RGBColor[0.9, 0.864, 0.81],
|
|
"MetallicCoefficient" -> 1,
|
|
"RoughnessCoefficient" -> 0.75,
|
|
"SpecularAnisotropyCoefficient" -> 0.3,
|
|
"Reflectance" -> 0.5,
|
|
"CoatColor" -> {1.0, 1.0, 1.0},
|
|
"CoatRoughnessCoefficient" -> 0.0,
|
|
"CoatAnisotropyCoefficient" -> 0.0,
|
|
"CoatReflectance" -> 0.5,
|
|
"SpecularColorMultiplier" -> 1,
|
|
"DiffuseColorMultiplier" -> 1.0,
|
|
"CoatColorMultiplier" -> 0.0
|
|
|>;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*Silver*)
|
|
|
|
|
|
materialParameters["Silver"] = <|
|
|
"BaseColorFront" -> RGBColor[1.0, 1.0, 1.0],
|
|
"BaseColorBack" -> RGBColor[1.0, 1.0, 1.0],
|
|
"BaseColorClosed" -> RGBColor[1.0, 1.0, 1.0],
|
|
"MetallicCoefficient" -> 1,
|
|
"RoughnessCoefficient" -> 0.75,
|
|
"SpecularAnisotropyCoefficient" -> 0.3,
|
|
"Reflectance" -> 0.5,
|
|
"CoatColor" -> {1.0, 1.0, 1.0},
|
|
"CoatRoughnessCoefficient" -> 0.0,
|
|
"CoatAnisotropyCoefficient" -> 0.0,
|
|
"CoatReflectance" -> 0.5,
|
|
"SpecularColorMultiplier" -> 1,
|
|
"DiffuseColorMultiplier" -> 1.0,
|
|
"CoatColorMultiplier" -> 0.0
|
|
|>;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*Clay*)
|
|
|
|
|
|
materialParameters["Clay"] = <|
|
|
"BaseColorFront" -> RGBColor[0.8, 0.352, 0.16],
|
|
"BaseColorBack" -> RGBColor[0.8, 0.352, 0.16],
|
|
"BaseColorClosed" -> RGBColor[0.8, 0.352, 0.16],
|
|
"MetallicCoefficient" -> 0,
|
|
"RoughnessCoefficient" -> 0,
|
|
"SpecularAnisotropyCoefficient" -> 0.0,
|
|
"Reflectance" -> 0.5,
|
|
"CoatColor" -> {1.0, 1.0, 1.0},
|
|
"CoatRoughnessCoefficient" -> 0.0,
|
|
"CoatAnisotropyCoefficient" -> 0.0,
|
|
"CoatReflectance" -> 0.5,
|
|
"SpecularColorMultiplier" -> 0.0,
|
|
"DiffuseColorMultiplier" -> 1.0,
|
|
"CoatColorMultiplier" -> 0.0
|
|
|>;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*Foil*)
|
|
|
|
|
|
materialParameters["Foil"] = <|
|
|
"BaseColorFront" -> RGBColor[0.5, 1.0, 0.0],
|
|
"BaseColorBack" -> RGBColor[0.5, 1.0, 0.0],
|
|
"BaseColorClosed" -> RGBColor[0.5, 1.0, 0.0],
|
|
"MetallicCoefficient" -> 0.5,
|
|
"RoughnessCoefficient" -> 0.6,
|
|
"SpecularAnisotropyCoefficient" -> 0.0,
|
|
"Reflectance" -> 0.5,
|
|
"CoatColor" -> {1.0, 1.0, 1.0},
|
|
"CoatRoughnessCoefficient" -> 0.6,
|
|
"CoatAnisotropyCoefficient" -> 0.0,
|
|
"CoatReflectance" -> 0.5,
|
|
"SpecularColorMultiplier" -> 1,
|
|
"DiffuseColorMultiplier" -> 1.0,
|
|
"CoatColorMultiplier" -> 0.75
|
|
|>;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*Glazed*)
|
|
|
|
|
|
materialParameters["Glazed"] = <|
|
|
"BaseColorFront" -> RGBColor[1.0, 0.24, 0.0],
|
|
"BaseColorBack" -> RGBColor[1.0, 0.24, 0.0],
|
|
"BaseColorClosed" -> RGBColor[1.0, 0.24, 0.0],
|
|
"MetallicCoefficient" -> 0.5,
|
|
"RoughnessCoefficient" -> 0.6,
|
|
"SpecularAnisotropyCoefficient" -> 0.6,
|
|
"Reflectance" -> 0.5,
|
|
"CoatColor" -> {1.0, 1.0, 1.0},
|
|
"CoatRoughnessCoefficient" -> 0.2,
|
|
"CoatAnisotropyCoefficient" -> 0.6,
|
|
"CoatReflectance" -> 0.6,
|
|
"SpecularColorMultiplier" -> 1.0,
|
|
"DiffuseColorMultiplier" -> 1.0,
|
|
"CoatColorMultiplier" -> 0.75
|
|
|>;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*Plastic*)
|
|
|
|
|
|
materialParameters["Plastic"] = <|
|
|
"BaseColorFront" -> RGBColor[0.3, 0.58, 1.0],
|
|
"BaseColorBack" -> RGBColor[0.3, 0.58, 1.0],
|
|
"BaseColorClosed" -> RGBColor[0.3, 0.58, 1.0],
|
|
"MetallicCoefficient" -> 0.0,
|
|
"RoughnessCoefficient" -> 1.0,
|
|
"SpecularAnisotropyCoefficient" -> 0.0,
|
|
"Reflectance" -> 0.5,
|
|
"CoatColor" -> {1.0, 1.0, 1.0},
|
|
"CoatRoughnessCoefficient" -> 0.3,
|
|
"CoatAnisotropyCoefficient" -> 0.5,
|
|
"CoatReflectance" -> 0.5,
|
|
"SpecularColorMultiplier" -> 1.0,
|
|
"DiffuseColorMultiplier" -> 1.0,
|
|
"CoatColorMultiplier" -> 0.75
|
|
|>;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*Rubber*)
|
|
|
|
|
|
materialParameters["Rubber"] = <|
|
|
"BaseColorFront" -> RGBColor[0.5, 0.5, 0.5],
|
|
"BaseColorBack" -> RGBColor[0.5, 0.5, 0.5],
|
|
"BaseColorClosed" -> RGBColor[0.5, 0.5, 0.5],
|
|
"MetallicCoefficient" -> 0.2,
|
|
"RoughnessCoefficient" -> 0.6,
|
|
"SpecularAnisotropyCoefficient" -> 0.0,
|
|
"Reflectance" -> 0.5,
|
|
"CoatColor" -> {1.0, 1.0, 1.0},
|
|
"CoatRoughnessCoefficient" -> 0.0,
|
|
"CoatAnisotropyCoefficient" -> 0.0,
|
|
"CoatReflectance" -> 0.5,
|
|
"SpecularColorMultiplier" -> 1,
|
|
"DiffuseColorMultiplier" -> 1.0,
|
|
"CoatColorMultiplier" -> 0.0
|
|
|>;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*Satin*)
|
|
|
|
|
|
materialParameters["Satin"] = <|
|
|
"BaseColorFront" -> RGBColor[0.75, 0.5, 1.0],
|
|
"BaseColorBack" -> RGBColor[0.75, 0.5, 1.0],
|
|
"BaseColorClosed" -> RGBColor[0.75, 0.5, 1.0],
|
|
"MetallicCoefficient" -> 0.5,
|
|
"RoughnessCoefficient" -> 0.66,
|
|
"SpecularAnisotropyCoefficient" -> 0.8,
|
|
"Reflectance" -> 0.5,
|
|
"CoatColor" -> {1.0, 1.0, 1.0},
|
|
"CoatRoughnessCoefficient" -> 0.0,
|
|
"CoatAnisotropyCoefficient" -> 0.0,
|
|
"CoatReflectance" -> 0.5,
|
|
"SpecularColorMultiplier" -> 1.0,
|
|
"DiffuseColorMultiplier" -> 1.0,
|
|
"CoatColorMultiplier" -> 0.0
|
|
|>;
|
|
|
|
|
|
(* ::Subsubsection::Closed:: *)
|
|
(*Base materials*)
|
|
|
|
|
|
$baseMaterials = Keys[materialParameters];
|
|
|
|
|
|
(* ::Subsection::Closed:: *)
|
|
(*messageRenderFunction*)
|
|
|
|
|
|
Options[messageRenderFunction] = Options[OpenVDBLevelSetRender];
|
|
|
|
|
|
messageRenderFunction[head_, expr_, ___] /; messageScalarGridQ[expr, head] = $Failed;
|
|
|
|
|
|
messageRenderFunction[head_, expr_, ___] /; messageLevelSetGridQ[expr, head] = $Failed;
|
|
|
|
|
|
messageRenderFunction[head_, vdb_, opts:OptionsPattern[]] := messageRenderFunction[head, vdb, Automatic, opts]
|
|
|
|
|
|
messageRenderFunction[head_, vdb_, shading_, opts:OptionsPattern[]] /; !OptionQ[shading] :=
|
|
Block[{assoc, opt},
|
|
assoc = iparseRenderOptions[vdb, shading, opts];
|
|
(
|
|
opt = renderFailureOption[assoc];
|
|
(
|
|
If[opt === "Shader",
|
|
Message[head::shaderval, shading],
|
|
Message[head::renderval, opt -> OptionValue[opt]]
|
|
];
|
|
$Failed
|
|
|
|
) /; opt =!= $Failed
|
|
|
|
) /; AssociationQ[assoc]
|
|
]
|
|
|
|
|
|
messageRenderFunction[___] = $Failed
|
|
|
|
|
|
General::renderval = "`1` is an invalid render setting.";
|
|
General::shaderval = "`1` is an invalid shader setting.";
|