diff --git a/generator/edit/examples/shapurr.json b/generator/edit/examples/shapurr.json index cde6fadd..d7d8fedd 100644 --- a/generator/edit/examples/shapurr.json +++ b/generator/edit/examples/shapurr.json @@ -1 +1 @@ -{"buffers":[{"byteLength":0,"uri":"data:application/octet-stream;base64,"}],"data":{"metadata":{"nodes":{"Node-1":{"position":{"x":727,"y":-188}},"Node-10":{"position":{"x":-616,"y":349}},"Node-12":{"position":{"x":-367,"y":-76}},"Node-13":{"position":{"x":1218,"y":-142}},"Node-14":{"position":{"x":-620,"y":564}},"Node-15":{"position":{"x":-368,"y":341}},"Node-16":{"position":{"x":-594,"y":793}},"Node-17":{"position":{"x":170,"y":-223}},"Node-18":{"position":{"x":37,"y":1674}},"Node-19":{"position":{"x":1560,"y":911}},"Node-2":{"position":{"x":1572,"y":-254}},"Node-20":{"position":{"x":656,"y":1738}},"Node-21":{"position":{"x":1235,"y":-704}},"Node-22":{"position":{"x":249,"y":1732}},"Node-23":{"position":{"x":1030,"y":-622}},"Node-24":{"position":{"x":1033,"y":-514}},"Node-25":{"position":{"x":1615,"y":1208}},"Node-26":{"position":{"x":1143,"y":1109}},"Node-27":{"position":{"x":840,"y":1202}},"Node-28":{"position":{"x":1356,"y":1512}},"Node-29":{"position":{"x":1109,"y":1527}},"Node-3":{"position":{"x":2304,"y":783}},"Node-30":{"position":{"x":1143,"y":922}},"Node-31":{"position":{"x":621,"y":901}},"Node-32":{"position":{"x":852,"y":998}},"Node-33":{"position":{"x":1130,"y":432}},"Node-34":{"position":{"x":871,"y":578}},"Node-35":{"position":{"x":860,"y":825}},"Node-36":{"position":{"x":387,"y":1378}},"Node-37":{"position":{"x":139,"y":1317}},"Node-38":{"position":{"x":-296,"y":2395}},"Node-39":{"position":{"x":-896,"y":2409}},"Node-40":{"position":{"x":-618,"y":2797}},"Node-41":{"position":{"x":-654,"y":2427}},"Node-42":{"position":{"x":-605,"y":2609}},"Node-43":{"position":{"x":-815,"y":2628}},"Node-44":{"position":{"x":-587,"y":1995}},"Node-45":{"position":{"x":-786,"y":2061}},"Node-46":{"position":{"x":-776,"y":2224}},"Node-47":{"position":{"x":1855,"y":2339}},"Node-48":{"position":{"x":1481,"y":2209}},"Node-49":{"position":{"x":1244,"y":2358}},"Node-5":{"position":{"x":482,"y":196}},"Node-50":{"position":{"x":1252,"y":2453}},"Node-51":{"position":{"x":1530,"y":2671}},"Node-52":{"position":{"x":1350,"y":2686}},"Node-53":{"position":{"x":1614,"y":2905}},"Node-54":{"position":{"x":-350,"y":-417}},"Node-55":{"position":{"x":-631,"y":-343}},"Node-56":{"position":{"x":-630,"y":-550}},"Node-57":{"position":{"x":-638,"y":-72}},"Node-58":{"position":{"x":-468,"y":-1075}},"Node-59":{"position":{"x":-718,"y":-844}},"Node-6":{"position":{"x":313,"y":-560}},"Node-60":{"position":{"x":-749,"y":-1053}},"Node-61":{"position":{"x":-743,"y":-1151}},"Node-62":{"position":{"x":-8,"y":1871}},"Node-63":{"position":{"x":-198,"y":1686}},"Node-64":{"position":{"x":982,"y":-150}},"Node-65":{"position":{"x":700,"y":59}},"Node-66":{"position":{"x":433,"y":1747}},"Node-67":{"position":{"x":-551,"y":1066}},"Node-68":{"position":{"x":-742,"y":1054}},"Node-69":{"position":{"x":-743,"y":1162}},"Node-70":{"position":{"x":476,"y":-72}},"Node-9":{"position":{"x":-29,"y":-228}}}},"nodes":{"Node-1":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/modeling/marching.MarchNode]","assignedInput":{"Domain":{"id":"Node-6","port":"Value"},"Field":{"id":"Node-70","port":"Union"},"Resolution":{"id":"Node-5","port":"Value"}}},"Node-10":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[github.com/EliCDavis/vector/vector3.Vector[float64]]","data":{"name":"","description":"","currentValue":{"x":0.24,"y":0.32919907960352635,"z":0.051100203537421535}}},"Node-12":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/sdf.RoundCubeNode]","assignedInput":{"Size":{"id":"Node-57","port":"Value"}}},"Node-13":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/modeling/meshops.SmoothNormalsImplicitWeldNode]","assignedInput":{"Mesh":{"id":"Node-64","port":"Out"}}},"Node-14":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[github.com/EliCDavis/vector/vector3.Vector[float64]]","data":{"name":"","description":"","currentValue":{"x":0.24,"y":0.7514109011273641,"z":0.2}}},"Node-15":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/sdf.RoundedConeNode]","assignedInput":{"A":{"id":"Node-10","port":"Value"},"B":{"id":"Node-14","port":"Value"},"Radius 1":{"id":"Node-16","port":"Value"}}},"Node-16":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[float64]","data":{"name":"","description":"","currentValue":0.3}},"Node-17":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/sdf.MirrorNode]","assignedInput":{"Field":{"id":"Node-9","port":"Union"}}},"Node-18":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/sdf.RoundCubeNode]","assignedInput":{"Size":{"id":"Node-63","port":"Value"}}},"Node-19":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/formats/gltf.ModelNode]","assignedInput":{"Children.0":{"id":"Node-38","port":"Out"},"Material":{"id":"Node-33","port":"Out"},"Mesh":{"id":"Node-20","port":"Out"},"Rotation":{"id":"Node-36","port":"Out"},"Scale":{"id":"Node-28","port":"Out"},"Translation":{"id":"Node-30","port":"Out"}}},"Node-2":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/formats/gltf.ModelNode]","assignedInput":{"Material":{"id":"Node-21","port":"Out"},"Mesh":{"id":"Node-13","port":"Out"}}},"Node-20":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/modeling/meshops.SmoothNormalsImplicitWeldNode]","assignedInput":{"Mesh":{"id":"Node-66","port":"Out"}}},"Node-21":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/formats/gltf.MaterialNode]","assignedInput":{"Color":{"id":"Node-23","port":"Value"},"Metallic Factor":{"id":"Node-24","port":"Float 64"}}},"Node-22":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/modeling/marching.MarchNode]","assignedInput":{"Field":{"id":"Node-18","port":"Field"},"Resolution":{"id":"Node-62","port":"Value"}}},"Node-23":{"type":"github.com/EliCDavis/polyform/generator/variable.VariableReferenceNode[github.com/EliCDavis/polyform/drawing/coloring.Color]","variable":"Fur Color"},"Node-24":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math.ZeroNode]"},"Node-25":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/formats/gltf.ModelNode]","assignedInput":{"Children.0":{"id":"Node-38","port":"Out"},"Material":{"id":"Node-33","port":"Out"},"Mesh":{"id":"Node-20","port":"Out"},"Rotation":{"id":"Node-36","port":"Out"},"Scale":{"id":"Node-28","port":"Out"},"Translation":{"id":"Node-26","port":"Out"}}},"Node-26":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/vector3.NewNode[float64]]","assignedInput":{"X":{"id":"Node-32","port":"Additive"},"Y":{"id":"Node-35","port":"Value"},"Z":{"id":"Node-27","port":"Value"}}},"Node-27":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[float64]","data":{"name":"","description":"","currentValue":0.4}},"Node-28":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/vector3.NewNode[float64]]","assignedInput":{"X":{"id":"Node-29","port":"Value"},"Y":{"id":"Node-29","port":"Value"},"Z":{"id":"Node-29","port":"Value"}}},"Node-29":{"type":"github.com/EliCDavis/polyform/generator/variable.VariableReferenceNode[float64]","variable":"Eye Size"},"Node-3":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/formats/gltf.ManifestNode]","assignedInput":{"Models.0":{"id":"Node-2","port":"Out"},"Models.1":{"id":"Node-19","port":"Out"},"Models.2":{"id":"Node-25","port":"Out"},"Models.3":{"id":"Node-47","port":"Out"}}},"Node-30":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/vector3.NewNode[float64]]","assignedInput":{"X":{"id":"Node-31","port":"Value"},"Y":{"id":"Node-35","port":"Value"},"Z":{"id":"Node-27","port":"Value"}}},"Node-31":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[float64]","data":{"name":"","description":"","currentValue":0.3}},"Node-32":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math.InverseNode[float64]]","assignedInput":{"In":{"id":"Node-31","port":"Value"}}},"Node-33":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/formats/gltf.MaterialNode]","assignedInput":{"Metallic Factor":{"id":"Node-34","port":"Float 64"}}},"Node-34":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math.ZeroNode]"},"Node-35":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[float64]","data":{"name":"","description":"","currentValue":0.15}},"Node-36":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/quaternion.FromEulerAngleNode]","assignedInput":{"Angle":{"id":"Node-37","port":"Value"}}},"Node-37":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[github.com/EliCDavis/vector/vector3.Vector[float64]]","data":{"name":"","description":"","currentValue":{"x":0,"y":0,"z":0.785}}},"Node-38":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/formats/gltf.ModelNode]","assignedInput":{"Material":{"id":"Node-44","port":"Out"},"Mesh":{"id":"Node-41","port":"Out"},"Scale":{"id":"Node-42","port":"Out"},"Translation":{"id":"Node-40","port":"Value"}}},"Node-39":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/modeling/primitives.UvSphereNode]"},"Node-40":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[github.com/EliCDavis/vector/vector3.Vector[float64]]","data":{"name":"","description":"","currentValue":{"x":0,"y":0,"z":0.5}}},"Node-41":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/modeling/meshops.SmoothNormalsImplicitWeldNode]","assignedInput":{"Mesh":{"id":"Node-39","port":"Out"}}},"Node-42":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/vector3.NewNode[float64]]","assignedInput":{"X":{"id":"Node-43","port":"Value"},"Y":{"id":"Node-43","port":"Value"},"Z":{"id":"Node-43","port":"Value"}}},"Node-43":{"type":"github.com/EliCDavis/polyform/generator/variable.VariableReferenceNode[float64]","variable":"Pupil Size"},"Node-44":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/formats/gltf.MaterialNode]","assignedInput":{"Color":{"id":"Node-45","port":"Value"},"Metallic Factor":{"id":"Node-46","port":"Float 64"}}},"Node-45":{"type":"github.com/EliCDavis/polyform/generator/variable.VariableReferenceNode[github.com/EliCDavis/polyform/drawing/coloring.Color]","variable":"Pupil Color"},"Node-46":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math.ZeroNode]"},"Node-47":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/formats/gltf.ModelNode]","assignedInput":{"Material":{"id":"Node-48","port":"Out"},"Mesh":{"id":"Node-20","port":"Out"},"Rotation":{"id":"Node-36","port":"Out"},"Scale":{"id":"Node-51","port":"Out"},"Translation":{"id":"Node-53","port":"Value"}}},"Node-48":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/formats/gltf.MaterialNode]","assignedInput":{"Color":{"id":"Node-49","port":"Value"},"Metallic Factor":{"id":"Node-50","port":"Float 64"}}},"Node-49":{"type":"github.com/EliCDavis/polyform/generator/variable.VariableReferenceNode[github.com/EliCDavis/polyform/drawing/coloring.Color]","variable":"Nose Color"},"Node-5":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[float64]","data":{"name":"","description":"","currentValue":20}},"Node-50":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math.ZeroNode]"},"Node-51":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/vector3.NewNode[float64]]","assignedInput":{"X":{"id":"Node-52","port":"Value"},"Y":{"id":"Node-52","port":"Value"},"Z":{"id":"Node-52","port":"Value"}}},"Node-52":{"type":"github.com/EliCDavis/polyform/generator/variable.VariableReferenceNode[float64]","variable":"Nose Size"},"Node-53":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[github.com/EliCDavis/vector/vector3.Vector[float64]]","data":{"name":"","description":"","currentValue":{"x":0,"y":-0.06736102152474865,"z":0.5169884495676479}}},"Node-54":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/sdf.SphereNode]","assignedInput":{"Position":{"id":"Node-56","port":"Value"},"Radius":{"id":"Node-55","port":"Value"}}},"Node-55":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[float64]","data":{"name":"","description":"","currentValue":0.4}},"Node-56":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[github.com/EliCDavis/vector/vector3.Vector[float64]]","data":{"name":"","description":"","currentValue":{"x":0,"y":-0.8523327784602677,"z":0}}},"Node-57":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[github.com/EliCDavis/vector/vector3.Vector[float64]]","data":{"name":"","description":"","currentValue":{"x":1,"y":0.8,"z":0.75}}},"Node-58":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/sdf.RoundedCylinderNode]","assignedInput":{"Body Height":{"id":"Node-61","port":"Value"},"Position":{"id":"Node-60","port":"Value"},"Radius":{"id":"Node-59","port":"Value"}}},"Node-59":{"type":"github.com/EliCDavis/polyform/generator/variable.VariableReferenceNode[float64]","variable":"Leg Radius"},"Node-6":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[github.com/EliCDavis/polyform/math/geometry.AABB]","data":{"name":"","description":"","currentValue":{"center":{"x":-1.0356143171090082,"y":0.06823937224703247,"z":0.3083300025606852},"extents":{"x":5.282742610160934,"y":2.477320071507882,"z":2.6306260493933773}}}},"Node-60":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[github.com/EliCDavis/vector/vector3.Vector[float64]]","data":{"name":"","description":"","currentValue":{"x":0.2037284400531858,"y":-1.057394398143054,"z":0}}},"Node-61":{"type":"github.com/EliCDavis/polyform/generator/variable.VariableReferenceNode[float64]","variable":"Leg Length"},"Node-62":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[float64]","data":{"name":"","description":"","currentValue":15}},"Node-63":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[github.com/EliCDavis/vector/vector3.Vector[float64]]","data":{"name":"","description":"","currentValue":{"x":0.7,"y":0.7,"z":0.7}}},"Node-64":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/modeling/meshops.LaplacianSmoothNode]","assignedInput":{"Mesh":{"id":"Node-1","port":"Mesh"},"Smoothing Factor":{"id":"Node-65","port":"Value"}}},"Node-65":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[float64]","data":{"name":"","description":"","currentValue":0.3}},"Node-66":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/modeling/meshops.LaplacianSmoothNode]","assignedInput":{"Mesh":{"id":"Node-22","port":"Mesh"}}},"Node-67":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/sdf.LinesNode]","assignedInput":{"Points":{"id":"Node-68","port":"Value"},"Radius":{"id":"Node-69","port":"Value"}}},"Node-68":{"type":"github.com/EliCDavis/polyform/generator/variable.VariableReferenceNode[[]github.com/EliCDavis/vector/vector3.Vector[float64]]","variable":"Tail Path"},"Node-69":{"type":"github.com/EliCDavis/polyform/generator/variable.VariableReferenceNode[float64]","variable":"Tail Width"},"Node-70":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/sdf.UnionNode]","assignedInput":{"Fields.0":{"id":"Node-17","port":"X"},"Fields.1":{"id":"Node-67","port":"Field"}}},"Node-9":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/sdf.UnionNode]","assignedInput":{"Fields.0":{"id":"Node-12","port":"Field"},"Fields.1":{"id":"Node-15","port":"Field"},"Fields.2":{"id":"Node-54","port":"Field"},"Fields.3":{"id":"Node-58","port":"Field"}}}},"producers":{},"variables":{"subgroups":{},"variables":{"Eye Size":{"description":"","data":{"type":"float64","value":0.45}},"Fur Color":{"description":"","data":{"type":"coloring.Color","value":"#01222b"}},"Leg Length":{"description":"","data":{"type":"float64","value":0.3}},"Leg Radius":{"description":"","data":{"type":"float64","value":0.05}},"Nose Color":{"description":"","data":{"type":"coloring.Color","value":"#9d5858"}},"Nose Size":{"description":"","data":{"type":"float64","value":0.15}},"Pupil Color":{"description":"","data":{"type":"coloring.Color","value":"#00000000"}},"Pupil Size":{"description":"","data":{"type":"float64","value":0.3}},"Tail Path":{"description":"","data":{"type":"[]vector3.Vector[float64]","value":[{"x":0,"y":-0.9095471764721286,"z":-0.4138844298990092},{"x":0.1589582469921156,"y":-1.2125666442807685,"z":-0.4169380236078606},{"x":0.3154083426128448,"y":-1.3141459237244673,"z":-0.26698896507187303},{"x":0.4750132103915383,"y":-1.3680494406048345,"z":0.11321283332987897}]}},"Tail Width":{"description":"","data":{"type":"float64","value":0.09}}}}}} \ No newline at end of file +{"buffers":[{"byteLength":0,"uri":"data:application/octet-stream;base64,"}],"data":{"metadata":{"nodes":{"Node-1":{"position":{"x":727,"y":-188}},"Node-10":{"position":{"x":-616,"y":349}},"Node-12":{"position":{"x":-367,"y":-76}},"Node-14":{"position":{"x":-620,"y":564}},"Node-15":{"position":{"x":-368,"y":341}},"Node-16":{"position":{"x":-594,"y":793}},"Node-17":{"position":{"x":164,"y":-226}},"Node-18":{"position":{"x":15,"y":1638}},"Node-19":{"position":{"x":1617,"y":859}},"Node-2":{"position":{"x":1572,"y":-254}},"Node-21":{"position":{"x":1235,"y":-704}},"Node-22":{"position":{"x":223,"y":1645}},"Node-23":{"position":{"x":1030,"y":-622}},"Node-24":{"position":{"x":1033,"y":-514}},"Node-25":{"position":{"x":1615,"y":1208}},"Node-26":{"position":{"x":1143,"y":1109}},"Node-28":{"position":{"x":1356,"y":1512}},"Node-29":{"position":{"x":1109,"y":1527}},"Node-3":{"position":{"x":2304,"y":783}},"Node-30":{"position":{"x":1143,"y":922}},"Node-32":{"position":{"x":835,"y":1003}},"Node-33":{"position":{"x":1130,"y":432}},"Node-34":{"position":{"x":871,"y":578}},"Node-35":{"position":{"x":860,"y":825}},"Node-36":{"position":{"x":310,"y":1077}},"Node-37":{"position":{"x":87,"y":1064}},"Node-38":{"position":{"x":-430,"y":2545}},"Node-39":{"position":{"x":-1099,"y":2643}},"Node-40":{"position":{"x":-821,"y":3031}},"Node-41":{"position":{"x":-857,"y":2661}},"Node-42":{"position":{"x":-808,"y":2843}},"Node-43":{"position":{"x":-1018,"y":2862}},"Node-44":{"position":{"x":-790,"y":2229}},"Node-45":{"position":{"x":-989,"y":2295}},"Node-46":{"position":{"x":-979,"y":2458}},"Node-47":{"position":{"x":1845,"y":2212}},"Node-48":{"position":{"x":1481,"y":2209}},"Node-49":{"position":{"x":1298,"y":2214}},"Node-5":{"position":{"x":454,"y":96}},"Node-50":{"position":{"x":1285,"y":2370}},"Node-51":{"position":{"x":1530,"y":2671}},"Node-52":{"position":{"x":1350,"y":2686}},"Node-53":{"position":{"x":1521,"y":2888}},"Node-54":{"position":{"x":-388,"y":-548}},"Node-55":{"position":{"x":-631,"y":-343}},"Node-56":{"position":{"x":-630,"y":-550}},"Node-57":{"position":{"x":-599,"y":-68}},"Node-58":{"position":{"x":-475,"y":-1150}},"Node-59":{"position":{"x":-718,"y":-844}},"Node-6":{"position":{"x":440,"y":-517}},"Node-60":{"position":{"x":-749,"y":-1053}},"Node-61":{"position":{"x":-743,"y":-1151}},"Node-62":{"position":{"x":11,"y":1864}},"Node-63":{"position":{"x":-210,"y":1773}},"Node-64":{"position":{"x":934,"y":-189}},"Node-65":{"position":{"x":700,"y":59}},"Node-66":{"position":{"x":-216,"y":1638}},"Node-67":{"position":{"x":163,"y":215}},"Node-68":{"position":{"x":-23,"y":214}},"Node-69":{"position":{"x":-24,"y":322}},"Node-70":{"position":{"x":476,"y":-72}},"Node-71":{"position":{"x":1166,"y":-192}},"Node-72":{"position":{"x":417,"y":1631}},"Node-73":{"position":{"x":656,"y":912}},"Node-74":{"position":{"x":802,"y":1180}},"Node-9":{"position":{"x":-29,"y":-228}}},"notes":{"1":{"position":{"x":-727,"y":-1241},"text":"# Legs\n\nTheir just cylinders","width":438.26851432213545},"2":{"position":{"x":-605,"y":263},"text":"# Ears\n\nIt's a cone","width":391.7908391744534},"3":{"position":{"x":-621,"y":-628},"text":"# Body\n\nIt's a sphere","width":367.3857266533418},"4":{"position":{"x":-591,"y":-121},"text":"# Hey Block Head\n\n","width":370.2483437313183},"5":{"position":{"x":-47,"y":-370},"text":"# Mirror\n\nMake one side of the body mirror the other. This makes it so we only have to modify one leg/ear, and we get the other one perfectly symetrical without any extra work","width":378.04456608867923},"6":{"position":{"x":-16,"y":139},"text":"# Tail\n\nIt's just a multipoint line","width":335.108526613877},"7":{"position":{"x":-1099,"y":2138},"text":"# Pupils\n\nIt's just a smoothed sphere. Nothing to it","width":813.1722922940445},"8":{"position":{"x":-199,"y":1533},"text":"# Eye + Nose Mesh\n\nGenerate a round cube mesh to use for both the eyes and nose","width":778.9960333995564},"9":{"position":{"x":1484,"y":2123},"text":"# Nose\n\nJust a rotated cube","width":525.5221516640636}}},"nodes":{"Node-1":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/modeling/marching.MarchNode]","assignedInput":{"Domain":{"id":"Node-6","port":"Value"},"Field":{"id":"Node-70","port":"Union"},"Resolution":{"id":"Node-5","port":"Value"}}},"Node-10":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[github.com/EliCDavis/vector/vector3.Vector[float64]]","data":{"name":"","description":"","currentValue":{"x":0.24,"y":0.32919907960352635,"z":0.051100203537421535}}},"Node-12":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/sdf.RoundCubeNode]","assignedInput":{"Size":{"id":"Node-57","port":"Value"}}},"Node-14":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[github.com/EliCDavis/vector/vector3.Vector[float64]]","data":{"name":"","description":"","currentValue":{"x":0.24,"y":0.7514109011273641,"z":0.2}}},"Node-15":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/sdf.RoundedConeNode]","assignedInput":{"A":{"id":"Node-10","port":"Value"},"B":{"id":"Node-14","port":"Value"},"Radius 1":{"id":"Node-16","port":"Value"}}},"Node-16":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[float64]","data":{"name":"","description":"","currentValue":0.3}},"Node-17":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/sdf.MirrorNode]","assignedInput":{"Field":{"id":"Node-9","port":"Union"}}},"Node-18":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/sdf.RoundCubeNode]","assignedInput":{"Roundness":{"id":"Node-66","port":"Value"},"Size":{"id":"Node-63","port":"Value"}}},"Node-19":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/formats/gltf.ModelNode]","assignedInput":{"Children.0":{"id":"Node-38","port":"Out"},"Material":{"id":"Node-33","port":"Out"},"Mesh":{"id":"Node-72","port":"Out"},"Rotation":{"id":"Node-36","port":"Out"},"Scale":{"id":"Node-28","port":"Out"},"Translation":{"id":"Node-30","port":"Out"}}},"Node-2":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/formats/gltf.ModelNode]","assignedInput":{"Material":{"id":"Node-21","port":"Out"},"Mesh":{"id":"Node-71","port":"Out"}}},"Node-21":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/formats/gltf.MaterialNode]","assignedInput":{"Color":{"id":"Node-23","port":"Value"},"Metallic Factor":{"id":"Node-24","port":"Float 64"}}},"Node-22":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/modeling/marching.MarchNode]","assignedInput":{"Field":{"id":"Node-18","port":"Field"},"Resolution":{"id":"Node-62","port":"Value"}}},"Node-23":{"type":"github.com/EliCDavis/polyform/generator/variable.VariableReferenceNode[github.com/EliCDavis/polyform/drawing/coloring.Color]","variable":"Fur Color"},"Node-24":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math.ZeroNode]"},"Node-25":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/formats/gltf.ModelNode]","assignedInput":{"Children.0":{"id":"Node-38","port":"Out"},"Material":{"id":"Node-33","port":"Out"},"Mesh":{"id":"Node-72","port":"Out"},"Rotation":{"id":"Node-36","port":"Out"},"Scale":{"id":"Node-28","port":"Out"},"Translation":{"id":"Node-26","port":"Out"}}},"Node-26":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/vector3.NewNode[float64]]","assignedInput":{"X":{"id":"Node-32","port":"Additive"},"Y":{"id":"Node-35","port":"Value"},"Z":{"id":"Node-74","port":"Value"}}},"Node-28":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/vector3.NewNode[float64]]","assignedInput":{"X":{"id":"Node-29","port":"Value"},"Y":{"id":"Node-29","port":"Value"},"Z":{"id":"Node-29","port":"Value"}}},"Node-29":{"type":"github.com/EliCDavis/polyform/generator/variable.VariableReferenceNode[float64]","variable":"Eye Size"},"Node-3":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/formats/gltf.ManifestNode]","assignedInput":{"Models.0":{"id":"Node-2","port":"Out"},"Models.1":{"id":"Node-19","port":"Out"},"Models.2":{"id":"Node-25","port":"Out"},"Models.3":{"id":"Node-47","port":"Out"}}},"Node-30":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/vector3.NewNode[float64]]","assignedInput":{"X":{"id":"Node-73","port":"Value"},"Y":{"id":"Node-35","port":"Value"},"Z":{"id":"Node-74","port":"Value"}}},"Node-32":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math.InverseNode[float64]]","assignedInput":{"In":{"id":"Node-73","port":"Value"}}},"Node-33":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/formats/gltf.MaterialNode]","assignedInput":{"Metallic Factor":{"id":"Node-34","port":"Float 64"}}},"Node-34":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math.ZeroNode]"},"Node-35":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[float64]","data":{"name":"","description":"","currentValue":0.15}},"Node-36":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/quaternion.FromEulerAngleNode]","assignedInput":{"Angle":{"id":"Node-37","port":"Value"}}},"Node-37":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[github.com/EliCDavis/vector/vector3.Vector[float64]]","data":{"name":"","description":"","currentValue":{"x":0,"y":0,"z":0.785}}},"Node-38":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/formats/gltf.ModelNode]","assignedInput":{"Material":{"id":"Node-44","port":"Out"},"Mesh":{"id":"Node-41","port":"Out"},"Scale":{"id":"Node-42","port":"Out"},"Translation":{"id":"Node-40","port":"Value"}}},"Node-39":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/modeling/primitives.UvSphereNode]"},"Node-40":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[github.com/EliCDavis/vector/vector3.Vector[float64]]","data":{"name":"","description":"","currentValue":{"x":0,"y":0,"z":0.4}}},"Node-41":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/modeling/meshops.SmoothNormalsImplicitWeldNode]","assignedInput":{"Mesh":{"id":"Node-39","port":"Out"}}},"Node-42":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/vector3.NewNode[float64]]","assignedInput":{"X":{"id":"Node-43","port":"Value"},"Y":{"id":"Node-43","port":"Value"},"Z":{"id":"Node-43","port":"Value"}}},"Node-43":{"type":"github.com/EliCDavis/polyform/generator/variable.VariableReferenceNode[float64]","variable":"Pupil Size"},"Node-44":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/formats/gltf.MaterialNode]","assignedInput":{"Color":{"id":"Node-45","port":"Value"},"Metallic Factor":{"id":"Node-46","port":"Float 64"}}},"Node-45":{"type":"github.com/EliCDavis/polyform/generator/variable.VariableReferenceNode[github.com/EliCDavis/polyform/drawing/coloring.Color]","variable":"Pupil Color"},"Node-46":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math.ZeroNode]"},"Node-47":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/formats/gltf.ModelNode]","assignedInput":{"Material":{"id":"Node-48","port":"Out"},"Mesh":{"id":"Node-72","port":"Out"},"Rotation":{"id":"Node-36","port":"Out"},"Scale":{"id":"Node-51","port":"Out"},"Translation":{"id":"Node-53","port":"Value"}}},"Node-48":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/formats/gltf.MaterialNode]","assignedInput":{"Color":{"id":"Node-49","port":"Value"},"Metallic Factor":{"id":"Node-50","port":"Float 64"}}},"Node-49":{"type":"github.com/EliCDavis/polyform/generator/variable.VariableReferenceNode[github.com/EliCDavis/polyform/drawing/coloring.Color]","variable":"Nose Color"},"Node-5":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[float64]","data":{"name":"","description":"","currentValue":20}},"Node-50":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math.ZeroNode]"},"Node-51":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/vector3.NewNode[float64]]","assignedInput":{"X":{"id":"Node-52","port":"Value"},"Y":{"id":"Node-52","port":"Value"},"Z":{"id":"Node-52","port":"Value"}}},"Node-52":{"type":"github.com/EliCDavis/polyform/generator/variable.VariableReferenceNode[float64]","variable":"Nose Size"},"Node-53":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[github.com/EliCDavis/vector/vector3.Vector[float64]]","data":{"name":"","description":"","currentValue":{"x":0,"y":-0.06736102152474865,"z":0.5169884495676479}}},"Node-54":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/sdf.SphereNode]","assignedInput":{"Position":{"id":"Node-56","port":"Value"},"Radius":{"id":"Node-55","port":"Value"}}},"Node-55":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[float64]","data":{"name":"","description":"","currentValue":0.4}},"Node-56":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[github.com/EliCDavis/vector/vector3.Vector[float64]]","data":{"name":"","description":"","currentValue":{"x":0,"y":-0.8523327784602677,"z":0}}},"Node-57":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[github.com/EliCDavis/vector/vector3.Vector[float64]]","data":{"name":"","description":"","currentValue":{"x":1,"y":0.8,"z":0.75}}},"Node-58":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/sdf.RoundedCylinderNode]","assignedInput":{"Body Height":{"id":"Node-61","port":"Value"},"Position":{"id":"Node-60","port":"Value"},"Radius":{"id":"Node-59","port":"Value"}}},"Node-59":{"type":"github.com/EliCDavis/polyform/generator/variable.VariableReferenceNode[float64]","variable":"Leg Radius"},"Node-6":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[github.com/EliCDavis/polyform/math/geometry.AABB]","data":{"name":"","description":"","currentValue":{"center":{"x":0.26869149581047447,"y":-0.21709643566139802,"z":0.31146070917968594},"extents":{"x":1.3528657227487297,"y":1.509920533927572,"z":1.121005529711819}}}},"Node-60":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[github.com/EliCDavis/vector/vector3.Vector[float64]]","data":{"name":"","description":"","currentValue":{"x":0.13355495773300535,"y":-1.057394398143054,"z":0}}},"Node-61":{"type":"github.com/EliCDavis/polyform/generator/variable.VariableReferenceNode[float64]","variable":"Leg Length"},"Node-62":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[float64]","data":{"name":"","description":"","currentValue":10}},"Node-63":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[github.com/EliCDavis/vector/vector3.Vector[float64]]","data":{"name":"","description":"","currentValue":{"x":0.5,"y":0.5,"z":0.5}}},"Node-64":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/modeling/meshops.LaplacianSmoothNode]","assignedInput":{"Mesh":{"id":"Node-1","port":"Mesh"},"Smoothing Factor":{"id":"Node-65","port":"Value"}}},"Node-65":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[float64]","data":{"name":"","description":"","currentValue":0.05}},"Node-66":{"type":"github.com/EliCDavis/polyform/generator/parameter.Value[float64]","data":{"name":"","description":"","currentValue":0.15}},"Node-67":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/sdf.LinesNode]","assignedInput":{"Points":{"id":"Node-68","port":"Value"},"Radius":{"id":"Node-69","port":"Value"}}},"Node-68":{"type":"github.com/EliCDavis/polyform/generator/variable.VariableReferenceNode[[]github.com/EliCDavis/vector/vector3.Vector[float64]]","variable":"Tail Path"},"Node-69":{"type":"github.com/EliCDavis/polyform/generator/variable.VariableReferenceNode[float64]","variable":"Tail Width"},"Node-70":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/sdf.UnionNode]","assignedInput":{"Fields.0":{"id":"Node-17","port":"X"},"Fields.1":{"id":"Node-67","port":"Field"}}},"Node-71":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/modeling/meshops.SmoothNormalsNode]","assignedInput":{"Mesh":{"id":"Node-64","port":"Out"}}},"Node-72":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/modeling/meshops.SmoothNormalsNode]","assignedInput":{"Mesh":{"id":"Node-22","port":"Mesh"}}},"Node-73":{"type":"github.com/EliCDavis/polyform/generator/variable.VariableReferenceNode[float64]","variable":"Eye Spacing"},"Node-74":{"type":"github.com/EliCDavis/polyform/generator/variable.VariableReferenceNode[float64]","variable":"Eye Protrusion"},"Node-9":{"type":"github.com/EliCDavis/polyform/nodes.Struct[github.com/EliCDavis/polyform/math/sdf.UnionNode]","assignedInput":{"Fields.0":{"id":"Node-12","port":"Field"},"Fields.1":{"id":"Node-15","port":"Field"},"Fields.2":{"id":"Node-54","port":"Field"},"Fields.3":{"id":"Node-58","port":"Field"}}}},"producers":{},"variables":{"subgroups":{},"variables":{"Eye Protrusion":{"description":"How far the eyeballs stick out of the head","data":{"type":"float64","value":0.35}},"Eye Size":{"description":"","data":{"type":"float64","value":0.5}},"Eye Spacing":{"description":"","data":{"type":"float64","value":0.275}},"Fur Color":{"description":"","data":{"type":"coloring.Color","value":"#01222b"}},"Leg Length":{"description":"","data":{"type":"float64","value":0.3}},"Leg Radius":{"description":"","data":{"type":"float64","value":0.05}},"Nose Color":{"description":"","data":{"type":"coloring.Color","value":"#9d5858"}},"Nose Size":{"description":"","data":{"type":"float64","value":0.15}},"Pupil Color":{"description":"","data":{"type":"coloring.Color","value":"#00000000"}},"Pupil Size":{"description":"","data":{"type":"float64","value":0.3}},"Tail Path":{"description":"","data":{"type":"[]vector3.Vector[float64]","value":[{"x":0,"y":-0.9095471764721286,"z":-0.3843793279856673},{"x":0.1589582469921156,"y":-1.2125666442807685,"z":-0.4169380236078606},{"x":0.3154083426128448,"y":-1.3141459237244673,"z":-0.26698896507187303},{"x":0.4750132103915383,"y":-1.3680494406048345,"z":0.11321283332987897}]}},"Tail Width":{"description":"","data":{"type":"float64","value":0.09}}}}}} \ No newline at end of file diff --git a/generator/edit/html/server.html b/generator/edit/html/server.html index 5bd59899..2dbd1e2d 100644 --- a/generator/edit/html/server.html +++ b/generator/edit/html/server.html @@ -80,6 +80,22 @@ z-index: 1; } + #running-message { + display: none; + position: absolute; + right: 20px; + bottom: 20px; + padding: 10px; + box-sizing: border-box; + text-align: center; + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + pointer-events: none; + z-index: 1; + } + #messageContainer { position: absolute; left: 0px; @@ -433,6 +449,9 @@
Polyform
+
+ Running... +
diff --git a/math/sdf/art.go b/math/sdf/art.go new file mode 100644 index 00000000..0e319c8b --- /dev/null +++ b/math/sdf/art.go @@ -0,0 +1,34 @@ +package sdf + +import ( + "github.com/EliCDavis/polyform/math/sample" + "github.com/EliCDavis/vector/vector3" +) + +// func displacementExample(p vector3.Float64) float64 { +// return math.Sin(20*p.X()) * math.Sin(20*p.Y()) * math.Sin(20*p.Z()) +// } + +func Displace(primitive, displacement sample.Vec3ToFloat, p vector3.Float64) float64 { + d1 := primitive(p) + d2 := displacement(p) + return d1 + d2 +} + +// func opTwist(primitive sample.Vec3ToFloat, p vector3.Float64) float64 { +// const k = 10.0 // or some other amount +// c := math.Cos(k * p.Y()) +// s := math.Sin(k * p.Y()) +// m := math.mat2(c, -s, s, c) +// q := vector3.New(m*p.xz, p.Y()) +// return primitive(q) +// } + +// func opCheapBend(primitive sample.Vec3ToFloat, p vector3.Float64) float64 { +// const k = 10.0 // or some other amount +// c := math.Cos(k * p.X()) +// s := math.Sin(k * p.X()) +// m := math.mat2(c, -s, s, c) +// q := vector3.New(m*p.xy, p.Z()) +// return primitive(q) +// } diff --git a/math/sdf/line.go b/math/sdf/line.go index 3290cd26..e68cee6e 100644 --- a/math/sdf/line.go +++ b/math/sdf/line.go @@ -1,8 +1,6 @@ package sdf import ( - "math" - "github.com/EliCDavis/polyform/math/geometry" "github.com/EliCDavis/polyform/math/sample" "github.com/EliCDavis/polyform/nodes" @@ -25,9 +23,7 @@ func MultipointLine(points []vector3.Float64, radius float64) sample.Vec3ToFloat switch len(points) { case 0: - return func(f vector3.Float64) float64 { - return math.Inf(1) - } + return nullField case 1: return Sphere(points[0], radius) diff --git a/math/sdf/nodes.go b/math/sdf/nodes.go index cb618a44..245f48ce 100644 --- a/math/sdf/nodes.go +++ b/math/sdf/nodes.go @@ -11,6 +11,7 @@ func init() { refutil.RegisterType[nodes.Struct[TranslateNode]](factory) refutil.RegisterType[nodes.Struct[TransformNode]](factory) + refutil.RegisterType[nodes.Struct[RepeatNode]](factory) refutil.RegisterType[nodes.Struct[UnionNode]](factory) refutil.RegisterType[nodes.Struct[IntersectionNode]](factory) diff --git a/math/sdf/repeat.go b/math/sdf/repeat.go new file mode 100644 index 00000000..8c32ef63 --- /dev/null +++ b/math/sdf/repeat.go @@ -0,0 +1,43 @@ +package sdf + +import ( + "github.com/EliCDavis/polyform/math/sample" + "github.com/EliCDavis/polyform/math/trs" + "github.com/EliCDavis/polyform/nodes" + "github.com/EliCDavis/vector/vector3" +) + +func Repeat(field sample.Vec3ToFloat, transforms []trs.TRS) sample.Vec3ToFloat { + if len(transforms) == 0 { + return nullField + } + + invertedTRS := make([]trs.TRS, len(transforms)) + for i, v := range transforms { + invertedTRS[i] = trs.FromMatrix(v.Matrix().Inverse()) + } + + return func(v vector3.Float64) float64 { + closestPoint := field(invertedTRS[0].Transform(v)) + for i := 1; i < len(invertedTRS); i++ { + closestPoint = min(closestPoint, field(invertedTRS[i].Transform(v))) + } + return closestPoint + } +} + +type RepeatNode struct { + Transforms nodes.Output[[]trs.TRS] + Field nodes.Output[sample.Vec3ToFloat] +} + +func (cn RepeatNode) Result(out *nodes.StructOutput[sample.Vec3ToFloat]) { + if cn.Field == nil { + return + } + + out.Set(Repeat( + nodes.GetOutputValue(out, cn.Field), + nodes.TryGetOutputValue(out, cn.Transforms, []trs.TRS{trs.Identity()}), + )) +} diff --git a/math/sdf/translate.go b/math/sdf/translate.go index 31cfd1aa..8e01a426 100644 --- a/math/sdf/translate.go +++ b/math/sdf/translate.go @@ -32,8 +32,9 @@ func (cn TranslateNode) Result(out *nodes.StructOutput[sample.Vec3ToFloat]) { // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< func Transform(field sample.Vec3ToFloat, transformation trs.TRS) sample.Vec3ToFloat { + inverse := transformation.Inverse() return func(v vector3.Float64) float64 { - return field(transformation.Transform(v)) + return field(inverse.Transform(v)) } } diff --git a/math/sdf/util.go b/math/sdf/util.go new file mode 100644 index 00000000..f487be99 --- /dev/null +++ b/math/sdf/util.go @@ -0,0 +1,12 @@ +package sdf + +import ( + "math" + + "github.com/EliCDavis/vector/vector3" +) + +// Field meant to represent "nothing". +func nullField(f vector3.Float64) float64 { + return math.Inf(1) +} diff --git a/math/trs/trs.go b/math/trs/trs.go index bc4621ad..7725149f 100644 --- a/math/trs/trs.go +++ b/math/trs/trs.go @@ -117,6 +117,10 @@ func (trs TRS) Multiply(other TRS) TRS { return FromMatrix(trs.Matrix().Multiply(other.Matrix())) } +func (trs TRS) Inverse() TRS { + return FromMatrix(trs.Matrix().Inverse()) +} + // Transform an array of points by the TRS func (trs TRS) TransformArray(in []vector3.Float64) []vector3.Float64 { out := make([]vector3.Float64, len(in)) diff --git a/modeling/marching/canvas.go b/modeling/marching/canvas.go index 14658745..ae5746c9 100644 --- a/modeling/marching/canvas.go +++ b/modeling/marching/canvas.go @@ -31,7 +31,8 @@ func interpolateV1(v1, v2, t float64) float64 { func interpolateVerts(v1, v2 vector3.Float64, v1v, v2v, cutoff float64) vector3.Float64 { t := interpolationValueFromCutoff(v1v, v2v, cutoff) - return v2.Sub(v1).Scale(t).Add(v1) + // return v2.Sub(v1).Scale(t).Add(v1) + return v1.Scale(1 - t).Add(v2.Scale(t)) } func lookupOrAdd(data *workingData, vert vector3.Float64) int { @@ -628,17 +629,18 @@ func (d *MarchingCanvas) marchFloat1BlockPosition( lookupIndex |= 128 } - for i := 0; triangulation[lookupIndex][i] != -1; i += 3 { + tris := triangulation[lookupIndex] + for i := 0; tris[i] != -1; i += 3 { // Get indices of corner points A and B for each of the three edges // of the cube that need to be joined to form the triangle. - a0 := cornerIndexAFromEdge[triangulation[lookupIndex][i]] - b0 := cornerIndexBFromEdge[triangulation[lookupIndex][i]] + a0 := cornerIndexAFromEdge[tris[i]] + b0 := cornerIndexBFromEdge[tris[i]] - a1 := cornerIndexAFromEdge[triangulation[lookupIndex][i+1]] - b1 := cornerIndexBFromEdge[triangulation[lookupIndex][i+1]] + a1 := cornerIndexAFromEdge[tris[i+1]] + b1 := cornerIndexBFromEdge[tris[i+1]] - a2 := cornerIndexAFromEdge[triangulation[lookupIndex][i+2]] - b2 := cornerIndexBFromEdge[triangulation[lookupIndex][i+2]] + a2 := cornerIndexAFromEdge[tris[i+2]] + b2 := cornerIndexBFromEdge[tris[i+2]] v1 := interpolateVerts(cubeCornerPositions[a0], cubeCornerPositions[b0], cubeCorners[a0], cubeCorners[b0], cutoff).Add(offset) v2 := interpolateVerts(cubeCornerPositions[a1], cubeCornerPositions[b1], cubeCorners[a1], cubeCorners[b1], cutoff).Add(offset) diff --git a/modeling/marching/march.go b/modeling/marching/march.go index 20aaabbb..de186969 100644 --- a/modeling/marching/march.go +++ b/modeling/marching/march.go @@ -6,38 +6,45 @@ import ( "github.com/EliCDavis/polyform/math/geometry" "github.com/EliCDavis/polyform/math/sample" "github.com/EliCDavis/polyform/modeling" + "github.com/EliCDavis/polyform/modeling/meshops" "github.com/EliCDavis/vector/vector3" ) -func marchRecurse(field sample.Vec3ToFloat, bounds geometry.AABB, cubeSize float64, res map[vector3.Int]float64) { - center := bounds.Center() +func marchRecurse(field sample.Vec3ToFloat, bounds geometry.AABB, cubeSize, surface float64, res map[vector3.Int]float64) { size := bounds.Size() + diagonal := size.Length() + + center := bounds.Center() + centerIndex := center.DivByConstant(cubeSize).RoundToInt() + recentered := centerIndex.ToFloat64().Scale(cubeSize) + + fieldResult := field(recentered) - surface + // TODO: WE THIS IS OUR BIGGEST SPEEDUP, FIGURE OUT HOW TO PRUNE HARDER // The closest surface is not within the bounds - fieldResult := field(center) - if math.Abs(fieldResult) > (size.MaxComponent()/2)+(cubeSize*2) { + if math.Abs(fieldResult) > (diagonal/2)+(cubeSize)+center.Distance(recentered) { return } - if size.MaxComponent() > cubeSize { - halfSize := size.Scale(0.5) - qs := halfSize.Scale(0.5) - marchRecurse(field, geometry.NewAABB(center.Add(vector3.New(qs.X(), qs.Y(), qs.Z())), halfSize), cubeSize, res) - marchRecurse(field, geometry.NewAABB(center.Add(vector3.New(qs.X(), qs.Y(), -qs.Z())), halfSize), cubeSize, res) - marchRecurse(field, geometry.NewAABB(center.Add(vector3.New(qs.X(), -qs.Y(), qs.Z())), halfSize), cubeSize, res) - marchRecurse(field, geometry.NewAABB(center.Add(vector3.New(qs.X(), -qs.Y(), -qs.Z())), halfSize), cubeSize, res) - marchRecurse(field, geometry.NewAABB(center.Add(vector3.New(-qs.X(), qs.Y(), qs.Z())), halfSize), cubeSize, res) - marchRecurse(field, geometry.NewAABB(center.Add(vector3.New(-qs.X(), qs.Y(), -qs.Z())), halfSize), cubeSize, res) - marchRecurse(field, geometry.NewAABB(center.Add(vector3.New(-qs.X(), -qs.Y(), qs.Z())), halfSize), cubeSize, res) - marchRecurse(field, geometry.NewAABB(center.Add(vector3.New(-qs.X(), -qs.Y(), -qs.Z())), halfSize), cubeSize, res) + res[centerIndex] = fieldResult + if size.MaxComponent() < cubeSize { return } - res[center.DivByConstant(cubeSize).FloorToInt()] = fieldResult + halfSize := size.Scale(0.5) + qs := halfSize.Scale(0.5) + marchRecurse(field, geometry.NewAABB(center.Add(vector3.New(qs.X(), qs.Y(), qs.Z())), halfSize), cubeSize, surface, res) + marchRecurse(field, geometry.NewAABB(center.Add(vector3.New(qs.X(), qs.Y(), -qs.Z())), halfSize), cubeSize, surface, res) + marchRecurse(field, geometry.NewAABB(center.Add(vector3.New(qs.X(), -qs.Y(), qs.Z())), halfSize), cubeSize, surface, res) + marchRecurse(field, geometry.NewAABB(center.Add(vector3.New(qs.X(), -qs.Y(), -qs.Z())), halfSize), cubeSize, surface, res) + marchRecurse(field, geometry.NewAABB(center.Add(vector3.New(-qs.X(), qs.Y(), qs.Z())), halfSize), cubeSize, surface, res) + marchRecurse(field, geometry.NewAABB(center.Add(vector3.New(-qs.X(), qs.Y(), -qs.Z())), halfSize), cubeSize, surface, res) + marchRecurse(field, geometry.NewAABB(center.Add(vector3.New(-qs.X(), -qs.Y(), qs.Z())), halfSize), cubeSize, surface, res) + marchRecurse(field, geometry.NewAABB(center.Add(vector3.New(-qs.X(), -qs.Y(), -qs.Z())), halfSize), cubeSize, surface, res) } func dedup(data *workingData, vert vector3.Float64, size float64) int { - distritized := vert.ToInt() + distritized := modeling.Vector3ToInt(vert, 4) if foundIndex, ok := data.vertLookup[distritized]; ok { return foundIndex @@ -49,20 +56,21 @@ func dedup(data *workingData, vert vector3.Float64, size float64) int { return index } -func March(field sample.Vec3ToFloat, domain geometry.AABB, cubeSize float64) modeling.Mesh { +func March(field sample.Vec3ToFloat, domain geometry.AABB, cubeSize, surface float64) modeling.Mesh { results := make(map[vector3.Int]float64) - marchRecurse(field, domain, cubeSize, results) + // sdfCompute := time.Now() + marchRecurse(field, domain, cubeSize, surface, results) + // log.Printf("Time To Compute SDFs %s", time.Since(sdfCompute)) + // marchCompute := time.Now() marchingWorkingData := &workingData{ tris: make([]int, 0), verts: make([]vector3.Float64, 0), vertLookup: make(map[vector3.Int]int), } - // tris := make([]int, 0) - // verts := make([]vector3.Float64, 0) - cubeCorners := make([]float64, 8) + cubeCornerPositions := make([]vector3.Float64, 8) for key, nnn := range results { cubeCorners[0] = nnn @@ -122,46 +130,40 @@ func March(field sample.Vec3ToFloat, domain geometry.AABB, cubeSize float64) mod lookupIndex |= 128 } + if lookupIndex == 0 || lookupIndex == 255 { + continue + } + xf := float64(key.X()) yf := float64(key.Y()) zf := float64(key.Z()) - cubeCornerPositions := []vector3.Float64{ - vector3.New(xf, yf, zf), - vector3.New(xf+1, yf, zf), - vector3.New(xf+1, yf, zf+1), - vector3.New(xf, yf, zf+1), - vector3.New(xf, yf+1, zf), - vector3.New(xf+1, yf+1, zf), - vector3.New(xf+1, yf+1, zf+1), - vector3.New(xf, yf+1, zf+1), - } - - for i := 0; triangulation[lookupIndex][i] != -1; i += 3 { + cubeCornerPositions[0] = vector3.New(xf, yf, zf) + cubeCornerPositions[1] = vector3.New(xf+1, yf, zf) + cubeCornerPositions[2] = vector3.New(xf+1, yf, zf+1) + cubeCornerPositions[3] = vector3.New(xf, yf, zf+1) + cubeCornerPositions[4] = vector3.New(xf, yf+1, zf) + cubeCornerPositions[5] = vector3.New(xf+1, yf+1, zf) + cubeCornerPositions[6] = vector3.New(xf+1, yf+1, zf+1) + cubeCornerPositions[7] = vector3.New(xf, yf+1, zf+1) + + tris := triangulation[lookupIndex] + for i := 0; tris[i] != -1; i += 3 { // Get indices of corner points A and B for each of the three edges // of the cube that need to be joined to form the triangle. - a0 := cornerIndexAFromEdge[triangulation[lookupIndex][i]] - b0 := cornerIndexBFromEdge[triangulation[lookupIndex][i]] + a0 := cornerIndexAFromEdge[tris[i]] + b0 := cornerIndexBFromEdge[tris[i]] - a1 := cornerIndexAFromEdge[triangulation[lookupIndex][i+1]] - b1 := cornerIndexBFromEdge[triangulation[lookupIndex][i+1]] + a1 := cornerIndexAFromEdge[tris[i+1]] + b1 := cornerIndexBFromEdge[tris[i+1]] - a2 := cornerIndexAFromEdge[triangulation[lookupIndex][i+2]] - b2 := cornerIndexBFromEdge[triangulation[lookupIndex][i+2]] + a2 := cornerIndexAFromEdge[tris[i+2]] + b2 := cornerIndexBFromEdge[tris[i+2]] v1 := interpolateVerts(cubeCornerPositions[a0], cubeCornerPositions[b0], cubeCorners[a0], cubeCorners[b0], 0) v2 := interpolateVerts(cubeCornerPositions[a1], cubeCornerPositions[b1], cubeCorners[a1], cubeCorners[b1], 0) v3 := interpolateVerts(cubeCornerPositions[a2], cubeCornerPositions[b2], cubeCorners[a2], cubeCorners[b2], 0) - // verts = append( - // verts, - // v1.Scale(cubeSize), - // v2.Scale(cubeSize), - // v3.Scale(cubeSize), - // ) - - // tris = append(tris, len(tris), len(tris)+1, len(tris)+2) - marchingWorkingData.tris = append( marchingWorkingData.tris, dedup(marchingWorkingData, v1, cubeSize), @@ -171,6 +173,14 @@ func March(field sample.Vec3ToFloat, domain geometry.AABB, cubeSize float64) mod } } - return modeling.NewMesh(modeling.TriangleTopology, marchingWorkingData.tris). + m := modeling.NewMesh(modeling.TriangleTopology, marchingWorkingData.tris). SetFloat3Attribute(modeling.PositionAttribute, marchingWorkingData.verts) + + if len(marchingWorkingData.tris) == 0 { + return m + } + + // log.Printf("Time To March Mesh %s", time.Since(marchCompute)) + + return meshops.RemoveNullFaces3D(m, modeling.PositionAttribute, 0) } diff --git a/modeling/marching/nodes.go b/modeling/marching/nodes.go index 2209a6d2..63579222 100644 --- a/modeling/marching/nodes.go +++ b/modeling/marching/nodes.go @@ -1,6 +1,8 @@ package marching import ( + "fmt" + "github.com/EliCDavis/polyform/generator" "github.com/EliCDavis/polyform/math/geometry" "github.com/EliCDavis/polyform/math/sample" @@ -19,9 +21,10 @@ func init() { } type MarchNode struct { - Field nodes.Output[sample.Vec3ToFloat] - Resolution nodes.Output[float64] - Domain nodes.Output[geometry.AABB] + Field nodes.Output[sample.Vec3ToFloat] `description:"The SDF to tesselate"` + Resolution nodes.Output[float64] `description:"Number of marching cube voxels contained in a single 'unit'"` + Surface nodes.Output[float64] `description:"value of the SDF that represents the surface (default: 0)"` + Domain nodes.Output[geometry.AABB] `description:"The region in which the marching cubes algorithm runs"` } func (cn MarchNode) Mesh(out *nodes.StructOutput[modeling.Mesh]) { @@ -30,30 +33,23 @@ func (cn MarchNode) Mesh(out *nodes.StructOutput[modeling.Mesh]) { return } - // canvas := NewMarchingCanvas( - // nodes.TryGetOutputValue(out, cn.Resolution, 1.), - // ) - - // canvas.AddField(Field{ - // Domain: nodes.TryGetOutputValue( - // out, - // cn.Domain, - // geometry.NewAABB(vector3.Zero[float64](), vector3.One[float64]()), - // ), - // Float1Functions: map[string]sample.Vec3ToFloat{ - // modeling.PositionAttribute: nodes.GetOutputValue(out, cn.Field), - // }, - // }) - - // out.Set(canvas.March(0)) + resolution := nodes.TryGetOutputValue(out, cn.Resolution, 1.) + if resolution <= 0 { + out.CaptureError(nodes.InvalidInputError{ + Input: cn.Resolution, + Message: fmt.Sprintf("value must be greater than 0 (recieved %f)", resolution), + }) + return + } out.Set(March( nodes.GetOutputValue(out, cn.Field), nodes.TryGetOutputValue( out, cn.Domain, - geometry.NewAABB(vector3.Zero[float64](), vector3.One[float64]()), + geometry.NewAABB(vector3.Zero[float64](), vector3.Fill(10.)), ), - 1/nodes.TryGetOutputValue(out, cn.Resolution, 1.), + 1/resolution, + nodes.TryGetOutputValue(out, cn.Surface, 0.), )) } diff --git a/modeling/meshops/smooth_normals.go b/modeling/meshops/smooth_normals.go index 1a65b8ff..b9423ff9 100644 --- a/modeling/meshops/smooth_normals.go +++ b/modeling/meshops/smooth_normals.go @@ -2,7 +2,6 @@ package meshops import ( "fmt" - "math" "github.com/EliCDavis/polyform/modeling" "github.com/EliCDavis/polyform/nodes" @@ -29,9 +28,6 @@ func SmoothNormals(m modeling.Mesh) modeling.Mesh { vertices := m.Float3Attribute(modeling.PositionAttribute) normals := make([]vector3.Float64, vertices.Len()) - for i := range normals { - normals[i] = vector3.Zero[float64]() - } tris := m.Indices() for triIndex := 0; triIndex < tris.Len(); triIndex += 3 { @@ -42,7 +38,7 @@ func SmoothNormals(m modeling.Mesh) modeling.Mesh { normalized := vertices.At(p2).Sub(vertices.At(p1)).Cross(vertices.At(p3).Sub(vertices.At(p1))) // This occurs whenever the given tri is actually just a line - if math.IsNaN(normalized.X()) { + if normalized.ContainsNaN() { continue } diff --git a/nodes/errors.go b/nodes/errors.go new file mode 100644 index 00000000..ff9d377e --- /dev/null +++ b/nodes/errors.go @@ -0,0 +1,12 @@ +package nodes + +import "fmt" + +type InvalidInputError struct { + Input OutputPort + Message string +} + +func (nie InvalidInputError) Error() string { + return fmt.Sprintf("invalid input %q: %s", nie.Input.Name(), nie.Message) +} diff --git a/website/ProducerView/producer_view_manager.ts b/website/ProducerView/producer_view_manager.ts index e8c12d87..78d807a1 100644 --- a/website/ProducerView/producer_view_manager.ts +++ b/website/ProducerView/producer_view_manager.ts @@ -1,489 +1,550 @@ -import { Box3, EquirectangularReflectionMapping, Group, Mesh, MeshStandardMaterial, PerspectiveCamera, Scene, SRGBColorSpace, TextureLoader, WebGLRenderer } from "three"; +import { + Box3, + EquirectangularReflectionMapping, + Group, + Mesh, + MeshStandardMaterial, + PerspectiveCamera, + Scene, + SRGBColorSpace, + TextureLoader, + WebGLRenderer, +} from "three"; import { ErrorManager } from "../error_manager"; import { InfoManager } from "../info_manager"; import { GraphInstance, Manifest, NodeDefinition } from "../schema"; import { getFileExtension } from "../utils"; import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js"; -import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js'; -import { PLYLoader } from 'three/examples/jsm/loaders/PLYLoader.js'; +import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader.js"; +import { PLYLoader } from "three/examples/jsm/loaders/PLYLoader.js"; import { RequestManager } from "../requests"; -import * as GaussianSplats3D from '@mkkellogg/gaussian-splats-3d'; +import * as GaussianSplats3D from "@mkkellogg/gaussian-splats-3d"; import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js"; import { ThreeApp } from "../three_app"; -import { SchemaManager } from '../schema_manager'; - +import { SchemaManager } from "../schema_manager"; type ProducerRefreshCallback = (string: string, thing: any) => void; - const textureLoader = new TextureLoader(); -const textureEquirec = textureLoader.load('https://i.imgur.com/Ev4X4yY_d.webp?maxwidth=1520&fidelity=grand'); +const textureEquirec = textureLoader.load( + "https://i.imgur.com/Ev4X4yY_d.webp?maxwidth=1520&fidelity=grand" +); textureEquirec.mapping = EquirectangularReflectionMapping; textureEquirec.colorSpace = SRGBColorSpace; - export class ProducerViewManager { + loadingCount: number; - loadingCount: number; + wireframe: boolean; - wireframe: boolean; + producerItemSubscriber: Array; - producerItemSubscriber: Array; + completeRefreshSubscriber: Array<() => void>; - completeRefreshSubscriber: Array<() => void>; + cachedSchema: GraphInstance; - cachedSchema: GraphInstance; + firstTimeLoadingScene: boolean; - firstTimeLoadingScene: boolean; + renderer: WebGLRenderer; - renderer: WebGLRenderer; + requestManager: RequestManager; - requestManager: RequestManager; + camera: PerspectiveCamera; - camera: PerspectiveCamera; + guassianSplatViewer: GaussianSplats3D.Viewer; - guassianSplatViewer: GaussianSplats3D.Viewer; + producerScene: Group; - producerScene: Group; + orbitControls: OrbitControls; - orbitControls: OrbitControls; + viewerContainer: Group; - viewerContainer: Group; + scene: Scene; - scene: Scene; + nodeTypeManifestPorts: Map; - nodeTypeManifestPorts: Map; + modelVersion: number; - modelVersion: number; - - schemaManager: SchemaManager; - - constructor( - app: ThreeApp, - requestManager: RequestManager, - nodeTypes: Array, - schemaManager: SchemaManager - ) { - this.completeRefreshSubscriber = []; - this.modelVersion = -1; - this.schemaManager = schemaManager; - this.nodeTypeManifestPorts = new Map(); - for (let i = 0; i < nodeTypes.length; i++) { - const nodeType = nodeTypes[i]; - if (!nodeType.outputs) { - continue; - } - for (const [outputName, output] of Object.entries(nodeType.outputs)) { - if (output.type === "github.com/EliCDavis/polyform/generator/manifest.Manifest") { - this.nodeTypeManifestPorts.set(nodeType.type, outputName); - } - } - } + readonly schemaManager: SchemaManager; - this.requestManager = requestManager; - this.renderer = app.Renderer; - this.camera = app.Camera; - this.orbitControls = app.OrbitControls; - this.viewerContainer = app.ViewerScene; - this.scene = app.Scene; - - this.producerScene = null; - this.wireframe = false; - this.firstTimeLoadingScene = true; - this.loadingCount = 0; - this.cachedSchema = null; - this.producerItemSubscriber = []; - } + readonly runningMessage: HTMLElement | undefined; - setModelVersion(newModelVersion: number): void { - if (newModelVersion === this.modelVersion) { - return; + constructor( + app: ThreeApp, + requestManager: RequestManager, + nodeTypes: Array, + schemaManager: SchemaManager + ) { + this.runningMessage = document.getElementById("running-message"); + this.completeRefreshSubscriber = []; + this.modelVersion = -1; + this.schemaManager = schemaManager; + this.nodeTypeManifestPorts = new Map(); + for (let i = 0; i < nodeTypes.length; i++) { + const nodeType = nodeTypes[i]; + if (!nodeType.outputs) { + continue; + } + for (const [outputName, output] of Object.entries(nodeType.outputs)) { + if ( + output.type === + "github.com/EliCDavis/polyform/generator/manifest.Manifest" + ) { + this.nodeTypeManifestPorts.set(nodeType.type, outputName); } - this.modelVersion = newModelVersion; - this.schemaManager.refreshSchema("Model version change"); + } } - SubscribeToProducerRefresh(callback: ProducerRefreshCallback): void { - this.producerItemSubscriber.push(callback); + this.requestManager = requestManager; + this.renderer = app.Renderer; + this.camera = app.Camera; + this.orbitControls = app.OrbitControls; + this.viewerContainer = app.ViewerScene; + this.scene = app.Scene; + + this.producerScene = null; + this.wireframe = false; + this.firstTimeLoadingScene = true; + this.loadingCount = 0; + this.cachedSchema = null; + this.producerItemSubscriber = []; + } + + setModelVersion(newModelVersion: number): void { + if (newModelVersion === this.modelVersion) { + return; } - - // Called whenever - SubscribeToCompleteRefresh(subsriber: () => void): void { - this.completeRefreshSubscriber.push(subsriber); + this.modelVersion = newModelVersion; + this.schemaManager.refreshSchema("Model version change"); + } + + SubscribeToProducerRefresh(callback: ProducerRefreshCallback): void { + this.producerItemSubscriber.push(callback); + } + + // Called whenever + SubscribeToCompleteRefresh(subsriber: () => void): void { + this.completeRefreshSubscriber.push(subsriber); + } + + private showRunningMessage() { + if (this.runningMessage) { + this.runningMessage.style.display = "block"; } + } - AddLoading(): void { - this.loadingCount += 1; + private hideRunningMessage() { + if (this.runningMessage) { + this.runningMessage.style.display = "none"; } + } - RemoveLoading(): void { - if (this.loadingCount === 0) { - throw new Error("loading count already 0"); - } - this.loadingCount -= 1; + AddLoading(): void { + this.loadingCount += 1; + } - if (this.loadingCount === 0 && this.cachedSchema) { - this.Refresh(this.cachedSchema) - this.cachedSchema = null; - } else { - // We're all done loading!!! - for (let i = 0; i < this.completeRefreshSubscriber.length; i++) { - this.completeRefreshSubscriber[i](); - } - } - } - - CurrentlyLoading(): boolean { - return this.loadingCount > 0; + RemoveLoading(): void { + if (this.loadingCount === 0) { + throw new Error("loading count already 0"); } + this.loadingCount -= 1; - NewSchema(schema: GraphInstance): void { - if (this.CurrentlyLoading()) { - this.cachedSchema = schema; - return; + if (this.loadingCount === 0) { + if (this.cachedSchema) { + this.Refresh(this.cachedSchema); + this.cachedSchema = null; + } else { + // We're all done loading!!! + this.hideRunningMessage(); + for (let i = 0; i < this.completeRefreshSubscriber.length; i++) { + this.completeRefreshSubscriber[i](); } - this.Refresh(schema); + } } + } - Render(): void { - if (this.guassianSplatViewer) { - this.guassianSplatViewer.update(); - this.guassianSplatViewer.render(); - } - } + CurrentlyLoading(): boolean { + return this.loadingCount > 0; + } - loadText(producerURL: string) { - this.AddLoading(); - this.requestManager.fetchText( - producerURL, - (data) => { - InfoManager.ShowInfo(data); - this.RemoveLoading(); - this.UpdateSubscribers(producerURL, data); - }, - (error) => { - this.RemoveLoading(); - console.error("unable to load text", producerURL, error); - ErrorManager.ShowError(producerURL, JSON.parse(error).error); - } - ); + NewSchema(schema: GraphInstance): void { + if (this.CurrentlyLoading()) { + this.cachedSchema = schema; + return; } + this.Refresh(schema); + } - loadImage(producerURL: string) { - this.AddLoading(); - this.requestManager.fetchImage( - producerURL, - (data) => { - this.RemoveLoading(); - this.UpdateSubscribers(producerURL, data); - }, - (error) => { - this.RemoveLoading(); - console.error("unable to load image", producerURL, error); - ErrorManager.ShowError(producerURL, JSON.parse(error).error); - } - ); + Render(): void { + if (this.guassianSplatViewer) { + this.guassianSplatViewer.update(); + this.guassianSplatViewer.render(); } - - viewAABB(aabb: Box3): void { - const aabbDepth = (aabb.max.z - aabb.min.z) - const aabbWidth = (aabb.max.x - aabb.min.x) - const aabbHeight = (aabb.max.y - aabb.min.y) - const aabbHalfHeight = aabbHeight / 2 - const mid = (aabb.max.y + aabb.min.y) / 2 - - if (this.firstTimeLoadingScene && isFinite(aabbWidth) && isFinite(aabbDepth) && isFinite(aabbHeight)) { - // console.log("Camera position intialized", aabbWidth, aabbDepth, aabbHeight); - this.firstTimeLoadingScene = false; - - this.camera.position.y = (- mid + aabbHalfHeight) * (3 / 2); - this.camera.position.z = Math.sqrt( - (aabbWidth * aabbWidth) + - (aabbDepth * aabbDepth) + - (aabbHeight * aabbHeight) - ) / 2; - - this.orbitControls.target.set( - (aabb.max.x + aabb.min.x) / 2, - - mid + aabbHalfHeight, - (aabb.max.z + aabb.min.z) / 2 - ); - this.orbitControls.update(); - } + } + + loadText(producerURL: string) { + this.AddLoading(); + this.requestManager.fetchText( + producerURL, + (data) => { + InfoManager.ShowInfo(data); + this.RemoveLoading(); + this.UpdateSubscribers(producerURL, data); + }, + (error) => { + this.RemoveLoading(); + console.error("unable to load text", producerURL, error); + ErrorManager.ShowError(producerURL, JSON.parse(error).error); + } + ); + } + + loadImage(producerURL: string) { + this.AddLoading(); + this.requestManager.fetchImage( + producerURL, + (data) => { + this.RemoveLoading(); + this.UpdateSubscribers(producerURL, data); + }, + (error) => { + this.RemoveLoading(); + console.error("unable to load image", producerURL, error); + ErrorManager.ShowError(producerURL, JSON.parse(error).error); + } + ); + } + + viewAABB(aabb: Box3): void { + const aabbDepth = aabb.max.z - aabb.min.z; + const aabbWidth = aabb.max.x - aabb.min.x; + const aabbHeight = aabb.max.y - aabb.min.y; + const aabbHalfHeight = aabbHeight / 2; + const mid = (aabb.max.y + aabb.min.y) / 2; + + if ( + this.firstTimeLoadingScene && + isFinite(aabbWidth) && + isFinite(aabbDepth) && + isFinite(aabbHeight) + ) { + // console.log("Camera position intialized", aabbWidth, aabbDepth, aabbHeight); + this.firstTimeLoadingScene = false; + + this.camera.position.y = (-mid + aabbHalfHeight) * (3 / 2); + this.camera.position.z = + Math.sqrt( + aabbWidth * aabbWidth + + aabbDepth * aabbDepth + + aabbHeight * aabbHeight + ) / 2; + + this.orbitControls.target.set( + (aabb.max.x + aabb.min.x) / 2, + -mid + aabbHalfHeight, + (aabb.max.z + aabb.min.z) / 2 + ); + this.orbitControls.update(); } + } + + loadObj(objLoader: OBJLoader, key: string, producerURL: string): void { + this.AddLoading(); + + objLoader.load( + producerURL, + (obj) => { + this.RemoveLoading(); + this.cleanProducerScene(); + + const aabb = new Box3(); + aabb.setFromObject(obj); + const aabbHeight = aabb.max.y - aabb.min.y; + const aabbHalfHeight = aabbHeight / 2; + const mid = (aabb.max.y + aabb.min.y) / 2; + + this.producerScene.add(obj); + + // We have to do this weird thing because the pivot of the scene + // Isn't always the center of the AABB + this.viewerContainer.position.set(0, -mid + aabbHalfHeight, 0); + + this.viewAABB(aabb); + }, + undefined, + (err) => { + this.cleanProducerScene(); + console.error(err); + this.RemoveLoading(); + } + ); + } + + loadGltf(gltfLoader: GLTFLoader, key: string, producerURL: string) { + this.AddLoading(); + gltfLoader.load( + producerURL, + ((gltf) => { + this.cleanProducerScene(); + + const aabb = new Box3(); + aabb.setFromObject(gltf.scene); + const aabbHeight = aabb.max.y - aabb.min.y; + const aabbHalfHeight = aabbHeight / 2; + const mid = (aabb.max.y + aabb.min.y) / 2; + + this.producerScene.add(gltf.scene); + + // We have to do this weird thing because the pivot of the scene + // Isn't always the center of the AABB + this.viewerContainer.position.set(0, -mid + aabbHalfHeight + 0.001, 0); + + const objects = []; + + gltf.scene.traverse((object) => { + if (object.isMesh) { + object.castShadow = true; + object.receiveShadow = true; + object.material.wireframe = this.wireframe; + object.material.envMap = textureEquirec; + object.material.needsUpdate = true; + // object.material.transparent = true; + + objects.push(object); + } else if (object.isPoints) { + object.material.size = 2; + } + }); - loadObj(objLoader: OBJLoader, key: string, producerURL: string): void { - this.AddLoading(); + // progressiveSurfacemap.addObjectsToLightMap(objects); - objLoader.load( - producerURL, - ((obj) => { - this.RemoveLoading(); - - const aabb = new Box3(); - aabb.setFromObject(obj); - const aabbHeight = (aabb.max.y - aabb.min.y) - const aabbHalfHeight = aabbHeight / 2 - const mid = (aabb.max.y + aabb.min.y) / 2 - - this.producerScene.add(obj); - - // We have to do this weird thing because the pivot of the scene - // Isn't always the center of the AABB - this.viewerContainer.position.set(0, - mid + aabbHalfHeight, 0); - - this.viewAABB(aabb); - }), - undefined, - (err) => { - console.error(err); - this.RemoveLoading(); - } - ) - } + this.viewAABB(aabb); - loadGltf(gltfLoader: GLTFLoader, key: string, producerURL: string) { - this.AddLoading(); - gltfLoader.load( - producerURL, - ((gltf) => { - - const aabb = new Box3(); - aabb.setFromObject(gltf.scene); - const aabbHeight = (aabb.max.y - aabb.min.y) - const aabbHalfHeight = aabbHeight / 2 - const mid = (aabb.max.y + aabb.min.y) / 2 - - this.producerScene.add(gltf.scene); - - // We have to do this weird thing because the pivot of the scene - // Isn't always the center of the AABB - this.viewerContainer.position.set(0, - mid + aabbHalfHeight + 0.001, 0) - - const objects = []; - - gltf.scene.traverse((object) => { - if (object.isMesh) { - object.castShadow = true; - object.receiveShadow = true; - object.material.wireframe = this.wireframe; - object.material.envMap = textureEquirec; - object.material.needsUpdate = true; - // object.material.transparent = true; - - objects.push(object) - } else if (object.isPoints) { - object.material.size = 2; - } - }); - - // progressiveSurfacemap.addObjectsToLightMap(objects); - - this.viewAABB(aabb); - - this.UpdateSubscribers(producerURL, gltf); - - this.RemoveLoading(); - }).bind(this), - undefined, - (error) => { - this.RemoveLoading(); - - if (typeof error === 'object' && "response" in error) { - var resp = error.response as any; - resp.json().then(x => { - ErrorManager.ShowError(key, x.error); - }) - } else { - console.error("Unkown error type from gltf loading", error) - } - - }); - } + this.UpdateSubscribers(producerURL, gltf); - loadPly(plyLoader: PLYLoader, key: string, producerURL: string): void { - this.AddLoading(); + this.RemoveLoading(); + }).bind(this), + undefined, + (error) => { + this.RemoveLoading(); + this.cleanProducerScene(); - plyLoader.load( - producerURL, - ((geometry) => { - this.RemoveLoading(); - geometry.computeVertexNormals(); - - const material = new MeshStandardMaterial({}); - const mesh = new Mesh(geometry, material); - mesh.castShadow = true; - mesh.receiveShadow = true; - - const aabb = new Box3(); - aabb.setFromObject(mesh); - const aabbHeight = (aabb.max.y - aabb.min.y) - const aabbHalfHeight = aabbHeight / 2 - const mid = (aabb.max.y + aabb.min.y) / 2 - - this.producerScene.add(mesh); - - // We have to do this weird thing because the pivot of the scene - // Isn't always the center of the AABB - this.viewerContainer.position.set(0, - mid + aabbHalfHeight, 0); - - this.viewAABB(aabb); - }), - undefined, - (err) => { - console.error(err); - this.RemoveLoading(); - } - ) - } - - loadSplat(key: string, producerURL: string): void { - this.AddLoading(); - if (this.guassianSplatViewer) { - this.guassianSplatViewer.dispose(); + if (typeof error === "object" && "response" in error) { + var resp = error.response as any; + resp.json().then((x) => { + ErrorManager.ShowError(key, x.error); + }); + } else { + console.error("Unkown error type from gltf loading", error); } + } + ); + } + + loadPly(plyLoader: PLYLoader, key: string, producerURL: string): void { + this.AddLoading(); + + plyLoader.load( + producerURL, + (geometry) => { + this.cleanProducerScene(); + + this.RemoveLoading(); + geometry.computeVertexNormals(); + + const material = new MeshStandardMaterial({}); + const mesh = new Mesh(geometry, material); + mesh.castShadow = true; + mesh.receiveShadow = true; + + const aabb = new Box3(); + aabb.setFromObject(mesh); + const aabbHeight = aabb.max.y - aabb.min.y; + const aabbHalfHeight = aabbHeight / 2; + const mid = (aabb.max.y + aabb.min.y) / 2; + + this.producerScene.add(mesh); + + // We have to do this weird thing because the pivot of the scene + // Isn't always the center of the AABB + this.viewerContainer.position.set(0, -mid + aabbHalfHeight, 0); + + this.viewAABB(aabb); + }, + undefined, + (err) => { + console.error(err); + this.cleanProducerScene(); + this.RemoveLoading(); + } + ); + } + + loadSplat(key: string, producerURL: string): void { + this.cleanProducerScene(); + + this.AddLoading(); + if (this.guassianSplatViewer) { + this.guassianSplatViewer.dispose(); + } - this.renderer.setPixelRatio(1); - - const wasm = true; - // https://github.com/mkkellogg/GaussianSplats3D/blob/main/src/Viewer.js - const splatViewerOptions = { - selfDrivenMode: false, - // 'cameraUp': [0, -1, 0], - sphericalHarmonicsDegree: 2, - useBuiltInControls: false, - rootElement: this.renderer.domElement.parentElement, - renderer: this.renderer, - threeScene: this.scene, - camera: this.camera, - // 'sceneRevealMode': GaussianSplats3D.SceneRevealMode.Instant, - - gpuAcceleratedSort: !wasm, - sharedMemoryForWorkers: !wasm + this.renderer.setPixelRatio(1); + + const wasm = true; + // https://github.com/mkkellogg/GaussianSplats3D/blob/main/src/Viewer.js + const splatViewerOptions = { + selfDrivenMode: false, + // 'cameraUp': [0, -1, 0], + sphericalHarmonicsDegree: 2, + useBuiltInControls: false, + rootElement: this.renderer.domElement.parentElement, + renderer: this.renderer, + threeScene: this.scene, + camera: this.camera, + // 'sceneRevealMode': GaussianSplats3D.SceneRevealMode.Instant, + + gpuAcceleratedSort: !wasm, + sharedMemoryForWorkers: !wasm, + }; + + this.guassianSplatViewer = new GaussianSplats3D.Viewer(splatViewerOptions); + + // getSplatCenter + this.guassianSplatViewer + .addSplatScene(producerURL, { + // rotation: [1, 0, 0, 0], + // scale: [-1, -1, 1, 0], + // streamView: false + // showLoadingUI: false, + // 'scale': [0.25, 0.25, 0.25], + }) + .then( + (() => { + this.guassianSplatViewer.splatMesh.onSplatTreeReady((splatTree) => { + const tree = splatTree.subTrees[0]; + const aabb = new Box3(); + aabb.setFromPoints([tree.sceneMin, tree.sceneMax]); + const aabbHeight = aabb.max.y - aabb.min.y; + const aabbHalfHeight = aabbHeight / 2; + const mid = (aabb.max.y + aabb.min.y) / 2; + + const shiftY = -mid + aabbHalfHeight; + this.guassianSplatViewer.splatMesh.position.set(0, shiftY, 0); + this.viewerContainer.position.set(0, shiftY, 0); + + this.viewAABB(aabb); + }); + + this.RemoveLoading(); + this.UpdateSubscribers( + producerURL, + this.guassianSplatViewer.splatMesh + ); + }).bind(this) + ) + .catch((x) => { + console.error(x); + this.RemoveLoading(); + ErrorManager.ShowError(key, x.error); + }); + } + + SetWireframe(wireframe: boolean): void { + this.wireframe = wireframe; + this.producerScene.traverse((object) => { + // https://discourse.threejs.org/t/gltf-scene-traverse-property-ismesh-does-not-exist-on-type-object3d/27212 + if (object instanceof Mesh) { + object.material.wireframe = wireframe; + } + }); + } + + ManifestLoaded(nodeId: string, portName: string, manifest: Manifest): void { + const manifestUrl: string = `./manifest/${nodeId}/${portName}/`; + const fileToLoad = manifest.main; + const fileToLoadMetadata = manifest.entries[fileToLoad].metadata; + + ErrorManager.ClearError(manifest.main); + const fileExt = getFileExtension(manifest.main); + + switch (fileExt) { + case "txt": + this.loadText(manifestUrl + fileToLoad); + break; + + case "gltf": + case "glb": + const gltfLoader = new GLTFLoader().setPath(manifestUrl); + this.loadGltf(gltfLoader, fileToLoad, fileToLoad); + break; + + case "obj": + const objLoader = new OBJLoader().setPath(manifestUrl); + this.loadObj(objLoader, fileToLoad, fileToLoad); + break; + + case "splat": + this.loadSplat(fileToLoad, manifestUrl + fileToLoad); + break; + + case "ply": + if ( + fileToLoadMetadata && + fileToLoadMetadata["gaussianSplat"] === true + ) { + this.loadSplat(fileToLoad, manifestUrl + fileToLoad); + } else { + const plyLoader = new PLYLoader().setPath(manifestUrl); + this.loadPly(plyLoader, fileToLoad, fileToLoad); } + break; - this.guassianSplatViewer = new GaussianSplats3D.Viewer(splatViewerOptions); - - // getSplatCenter - this.guassianSplatViewer.addSplatScene(producerURL, { - // rotation: [1, 0, 0, 0], - // scale: [-1, -1, 1, 0], - // streamView: false - // showLoadingUI: false, - // 'scale': [0.25, 0.25, 0.25], - }).then((() => { - - this.guassianSplatViewer.splatMesh.onSplatTreeReady((splatTree) => { - const tree = splatTree.subTrees[0] - const aabb = new Box3(); - aabb.setFromPoints([tree.sceneMin, tree.sceneMax]); - const aabbHeight = (aabb.max.y - aabb.min.y) - const aabbHalfHeight = aabbHeight / 2 - const mid = (aabb.max.y + aabb.min.y) / 2 - - const shiftY = - mid + aabbHalfHeight - this.guassianSplatViewer.splatMesh.position.set(0, shiftY, 0) - this.viewerContainer.position.set(0, shiftY, 0); - - this.viewAABB(aabb); - }); - - this.RemoveLoading(); - this.UpdateSubscribers(producerURL, this.guassianSplatViewer.splatMesh); - - }).bind(this)).catch(x => { - console.error(x) - this.RemoveLoading(); - ErrorManager.ShowError(key, x.error); - }) + // case "png": + // this.loadImage(manifestUrl + fileToLoad); + // break; } + } - SetWireframe(wireframe: boolean): void { - this.wireframe = wireframe; - this.producerScene.traverse((object) => { - // https://discourse.threejs.org/t/gltf-scene-traverse-property-ismesh-does-not-exist-on-type-object3d/27212 - if (object instanceof Mesh) { - object.material.wireframe = wireframe; - } - }); + private cleanProducerScene() { + if (this.producerScene != null) { + this.viewerContainer.remove(this.producerScene); } - ManifestLoaded(nodeId: string, portName: string, manifest: Manifest): void { - - const manifestUrl: string = `./manifest/${nodeId}/${portName}/`; - const fileToLoad = manifest.main; - const fileToLoadMetadata = manifest.entries[fileToLoad].metadata - - ErrorManager.ClearError(manifest.main); - const fileExt = getFileExtension(manifest.main); - - switch (fileExt) { - case "txt": - this.loadText(manifestUrl + fileToLoad); - break; - - case "gltf": - case "glb": - const gltfLoader = new GLTFLoader().setPath(manifestUrl); - this.loadGltf(gltfLoader, fileToLoad, fileToLoad); - break; - - case "obj": - const objLoader = new OBJLoader().setPath(manifestUrl); - this.loadObj(objLoader, fileToLoad, fileToLoad); - break; - - case "splat": - this.loadSplat(fileToLoad, manifestUrl + fileToLoad) - break; - - case "ply": - if (fileToLoadMetadata && fileToLoadMetadata["gaussianSplat"] === true) { - this.loadSplat(fileToLoad, manifestUrl + fileToLoad) - } else { - const plyLoader = new PLYLoader().setPath(manifestUrl); - this.loadPly(plyLoader, fileToLoad, fileToLoad) - } - break; - - // case "png": - // this.loadImage(manifestUrl + fileToLoad); - // break; - } - } + this.producerScene = new Group(); + this.viewerContainer.add(this.producerScene); + } - Refresh(schema: GraphInstance) { - InfoManager.ClearInfo(); + Refresh(schema: GraphInstance) { + // this.cleanProducerScene(); + InfoManager.ClearInfo(); - if (this.producerScene != null) { - this.viewerContainer.remove(this.producerScene) - } - - this.producerScene = new Group(); - this.viewerContainer.add(this.producerScene); + this.showRunningMessage(); - for (const [nodeID, nodeInstance] of Object.entries(schema.nodes)) { + let hasManifests = false; - // This node doesn't have a manifest output, continue - if (!this.nodeTypeManifestPorts.has(nodeInstance.type)) { - continue; - } + for (const [nodeID, nodeInstance] of Object.entries(schema.nodes)) { + // This node doesn't have a manifest output, continue + if (!this.nodeTypeManifestPorts.has(nodeInstance.type)) { + continue; + } - const portName = this.nodeTypeManifestPorts.get(nodeInstance.type) - this.requestManager.getManifest(nodeID, portName, (manifest) => { - this.ManifestLoaded(nodeID, portName, manifest) - }); - } + hasManifests = true; + const portName = this.nodeTypeManifestPorts.get(nodeInstance.type); + this.requestManager.getManifest(nodeID, portName, (manifest) => { + this.ManifestLoaded(nodeID, portName, manifest); + }); } - - UpdateSubscribers(url: string, thing: any) { - this.producerItemSubscriber - .forEach(sub => { - if (!sub) { - return; - } - sub(url, thing); - }) + if (hasManifests) { + this.showRunningMessage(); } + } + + UpdateSubscribers(url: string, thing: any) { + this.producerItemSubscriber.forEach((sub) => { + if (!sub) { + return; + } + sub(url, thing); + }); + } } - diff --git a/website/nodes/node.ts b/website/nodes/node.ts index aab501f3..53e560d5 100644 --- a/website/nodes/node.ts +++ b/website/nodes/node.ts @@ -344,7 +344,6 @@ export class PolyNodeController { } return; } - console.log("Woo", outputPort.connections().length) } let found = false;