diff --git a/proposals/_notPublished/hold/LineStyle/README.md b/proposals/_notPublished/hold/LineStyle/README.md index f009962b..bf9456bb 100644 --- a/proposals/_notPublished/hold/LineStyle/README.md +++ b/proposals/_notPublished/hold/LineStyle/README.md @@ -1,76 +1,194 @@ # Introduction -USD already has primitives to render 3D curve-like geometries, such as hair or grass. In practice, we also need curves in sketch or CAD document. The curve has uniform width, and its width will not change when we rotate or zoom the sketch. The curve may also have dash-dot patterns. We will provide a schema which could be applied to the curves primitive, and the primitive will become a uniform screen-width curve, and can have dash-dot patterns, or other type of patterns(such as waveline). +USD already has primitives to render 3D curve-like geometries, such as hair or grass. In practice, there are requirements for curves with dash-dot patterns. The curve has uniform width, and its width will not change when we rotate or zoom the sketch. One common example is that when you do selection, you will see a rectangle that highlights the selection area, and the edge of the rectangle will have a dash pattern. Another common example is that in a CAD design document, the dash-dot lines can be used to show hidden lines. +In this proposal, we will provide a schema which is for patterned lines or polylines. The primitive will have uniform screen-width, and can have dash-dot patterns. -In this design, we don't consider any 3D-like curve styles, such as Blender's Grease Pencil or Tiltbrush. - -Here is a picture of common curve patterns. +Here is a picture of common dash-dot patterns. ![image of curve patterns](linePatterns.jpg) # Requirements -### Curve Width -The curve width is a screen-space width. It will not change when we zoom in or zoom out. The curve width is uniform across the whole curve. +### Line Width +The line width is a screen-space width. It will not change when we zoom in or zoom out. The line width is uniform across the whole line. -### Curve Caps -The curve cap is the shape at the start or end of a curve or dash. There are different types of curve cap. The value can be different for the start and the end. But all the start caps in a curve should be the same, and all the end caps should be the same. -The curve cap will also impact the shape of the dot in a curve pattern. The start cap is the shape of the left half of the dot, and the end cap is the shape of the right half of the dot. +### Line Caps +The line cap is the shape at the start or end of a line or dash. There are different types of line cap. The value can be different for the start and the end. But all the start caps in a line should be the same, and all the end caps should be the same. +The line cap will also impact the shape of the dot in a dash-dot pattern. The start cap is the shape of the left half of the dot, and the end cap is the shape of the right half of the dot. | cap type | round | square | triangle | |:--------:|:---------:|:-----------:|:----------:| | figure |![round](roundcap.png)|![square](rectanglecap.png)|![triangles](triangleoutcap.png)| -### Curve Joint -The curve joint is the shape at the joint of two curves, or at the joint of a polyline. The value is constant for the whole primitive. +### Line Joint +The line joint is the shape at the joint of two lines, or at the joint of a polyline. The value is constant for the whole primitive. -![image of curve joint](roundJoint.png) +![image of line joint](roundJoint.png) -### Curve Pattern +### Dash-dot Pattern A dash-dot pattern is a composite of dashes and dots and the composition is periodic. -You can also define other type of patterns. - -# The implementation of DashDot curve style -Our implementation will be only applied to a BasisCurve with "linear" type. That is, the curve is a list of line segments or a polyline. The width of the line will be uniform, and it will not change when camera changes. There can be no pattern, which is called "sketch" style. It can also have dash dot pattern, which is called "dashDot" style. - -To implement the curve style, we will add a style property to the BasisCurves, and add the special vertex and fragment shader. We will also provide two different materials: one is for the "sketch" style, and another is for the "dashDot" style. - -### Modification to the BasisCurves -A new property is added to BasisCuves schema: - -- style. A string uniform, which determines the type of the curve style. Currently its value could be "none", "sketch", "dashDot" and "screenSpaceDashDot". By default the value is "none", which means the curve doesn't have style. The value "sketch", "dashDot" and "screenSpaceDashDot" are only valid when the type is "linear". If the value is "sketch", the width of the curve will not change when camera changed. There is no pattern. If the value is "dashDot", the curve style pattern is dash dot pattern, and the pattern will be based on world unit. If we zoom in, the pattern on the curve will not change in world space. The dash size and the dash gap size in the screen space will be larger. If the value is "screenSpaceDashDot", the curve style pattern is dash dot pattern, and the pattern will be based on screen unit. If we zoom in, the pattern on the curve will change in the world space, so that the dash size and the dash gap size in the screen space will not change. - -![image of screenSpacePattern](screenSpacePattern.png) - -If the curve style is "sketch", "dashDot" or "screenSpaceDashDot", the curve must bind to a specific material. The property of the style, such as the cap shape or the scale of the dash dot pattern, will be set in the material via the surface input. - -In the implementation, we also create special geometry when the curve style is "sketch", "dashDot" or "screenSpaceDashDot". Each line segment is converted to a rectangle which is composed from two triangles. -The shader of the BasisCurves is also modified. We add two new sections of shader code: "Curves.Vertex.DashDot" and "Curves.Fragment.DashDot". If the curve style is "sketch", "dashDot" or "screenSpaceDashDot", the vertex shader must be "Curves.Vertex.DashDot" and the fragment shader must be "Curves.Fragment.DashDot". - -### Material to support the dash dot style -If the curve style is "sketch", it must contain a LineSketchSurface Shader. If the curve style is "dashDot" or "screenSpaceDashDot", it must contain a DashDotSurface Shader and DashDotTexture shader. - -### LineSketchSurface -The shader will decide the opacity of pixels around caps and joint. The materialTag for this material is translucent. - -This surface also has these special input: -- startCapType. An int input. It can be 0, 1, or 2. 0 means the cap type is round. 1 means the cap type is square. 2 means the cap type is triangle. The default value is 0. -- endCapType. An int input. It can be 0, 1, or 2. 0 means the cap type is round. 1 means the cap type is square. 2 means the cap type is triangle. The default value is 0. -- jointType. An int input. Currently it can only be 0, which means the joint type is round. - -### DashDotSurface -The shader will decide whether the pixel is within a dash or a gap, so that we can decide its opacity. It will also handle the caps and joint. The materialTag for this material is translucent. - -The DashDotSurface must has a color input, which connects to another shader whose shader is DashDotTexture. The DashDotTexture shader links to a texture which saves the information of the dash-dot pattern. - -This surface also has these special input: -- startCapType. An int input. It can be 0, 1, or 2. 0 means the cap type is round. 1 means the cap type is square. 2 means the cap type is triangle. The default value is 0. -- endCapType. An int input. It can be 0, 1, or 2. 0 means the cap type is round. 1 means the cap type is square. 2 means the cap type is triangle. The default value is 0. -- jointType. An int input. Currently it can only be 0, which means the joint type is round. -- patternScale. A float input. The default value is 1. You can lengthen or compress the curve pattern by setting this property. For example, if patternScale is set to 2, the length of each dash and each gap will be enlarged by 2 times. This value will not impact on the curve width. - -### DashDotTexture -The DashDotTexture shader is quite the same as UVTexture shader. The difference is that it outputs rgba value. The default value for wrap is clamp, and the default value for min/max filter is "nearest". The shader must have a dash-dot texture input. - -### The dash-dot texture -The dash-dot texture is a texture that saves a type of dash-dot pattern. In the four channels we will save if the pixel is within a dash or a gap, and the start and end position of the current dash. \ No newline at end of file +# The implementation of DashDot line style +Our implementation will introduce a new type of primitive, the DashDotLines. It inherits from Curves. The primitive is a list of line segments or polylines. The width of the line will be uniform, and it will not change when camera changes. There can be no pattern in the line. Or there can be dash-dot pattern. + +The detail of the dash-dot pattern will be put in the DashDotPatternAPI. + +We also add a new rprim for the DashDotLines. We add a two shader files, the dashDotLines.glslfx, which includes both vertex and fragment shader, and dashDotFallbackMaterialNetwork.glslfx, which is a default material for the DashDotLines. + +We provide two different implementations. In the "allDetails" implementation, we create special geometry for the primitive. Each line segment is converted to a rectangle which is composed from two triangles. The dash-dot pattern and the line width are implemented in the shader. In the "noCapJoint" implementation, the line segments or polylines are rendered as line lists, and in each part of line, we will check if the pixel is within the dash or the gap. For this implementation, the caps and joints will be ignored. And the line width is implemented in the GPU pipeline configuration, such as using glLineWidth(). + +### The DashDotLines schema +A new primitive DashDotLines is added, which inherits from Curves. It inherits properties from Curves. +The shape of this primitive on the screen is a uniform-width line or polyline. Its width will not change when camera changes. It has either no pattern or dash-dot pattern. + +By default, the DashDotLines primitive doesn't have a pattern. If you would like to have a pattern, you need to create a "Pattern" primitive which will apply the DashDotPatternAPI schema, and you can configure the pattern properties. Then the DashDotLines primitive should inherit from the "Pattern" primitive. Different DashDotLines primitive could inherits from the same "Pattern" primitive. + +These are properties which inherited from Curves: +- curveVertexCounts +- widths. Widths are now interpreted as the widths in screen space. + +The DashDotLines has the following new properties: +- shapeDetail. A token uniform. It implies if the caps and joints are rendered or not. Currently it can be "allDetails" or "noCapJoint". The default value is "allDetails", which means the startCap, endCap and the joint will be rendered. If this value is "noCapJoint", the startCapType, endCapType and jointType will be ignored. +- startCapType. A token uniform. It is valid when shapeDetail is "allDetails". It is the shape of the line cap at the start of the line. It can be "round", "triangle" or "square". The default value is "round". +- endCapType. A token uniform. It is valid when shapeDetail is "allDetails". It is the shape of the line cap at the end of the line. It can be "round", "triangle" or "square". The default value is "round". +- jointType. A token uniform. It is valid when shapeDetail is "allDetails". It is the shape at the joint of two lines, or at the joint of a polyline. Currently it can only be "round". More types of joint can be added in the future. +- patternScale. A float uniform. It is valid when the primitive inherits from a "Pattern" primitive. The default value is 1. You can lengthen or compress the line pattern by setting this property. For example, if patternScale is set to 2, the length of each dash and each gap will be enlarged by 2 times. This value will not impact on the line width. +- screenspacePattern. A bool uniform. It is valid when the primitive inherits from a "Pattern" primitive. By default it is true, which means the dash-dot pattern will be based on screen unit. If we zoom in, the pattern on the line will change in the world space, so that the dash size and the dash gap size in the screen space will not change. If it is false, the pattern will be based on world unit. If we zoom in, the pattern on the line will not change in world space. The dash size and the dash gap size in the screen space will be larger. + +![image of screenspacePattern](screenSpacePattern.png) + +### Extents of the DashDotLines +Different from the other Curves, the extents of the DashDotLines is only the bound box of the control points. The width of the line will not be considered, because it is screen spaced, that it is implemented via the shader. + +### The DashDotPatternAPI schema +The DashDotPatternAPI schema inherits from APISchemaBase. It saves the detail of a dash-dot pattern. A dash-dot pattern is an array of symbols. Each symbol is either a dash, which is a line, or a dot, which is a point. + +The DashDotPatternAPI has the following new properties: +- patternPeriod. A float uniform. It is the length of a pattern. By default it is zero. If there is no pattern, it should be zero. +- pattern. An array of float2. It saves the dash-dot pattern. For each float2, the x value and the y value must be zero or positive. The x value saves the offset of the start of current symbol, from the end of the previous symbol. If the current symbol is the first symbol, the offset is from the start of the pattern to the start of current symbol. The y value saves the length of the current symbol. If it is zero, the current symbol is a dot. If it is larger than zero, the current symbol is a dash. As a result, the total sum of all the x value and y value will be the length from the start of the pattern to the end of the last symbol. This sum must be smaller than patternPeriod. + +For example, assume the pattern is [(0, 10), (1, 4), (3, 0)]. It means the first symbol is a dash which is from 0 to 10. The second symbol is a dash which is from 11 to 15, and the third symbol is a dot which is at position 18. There are gaps between 10 and 11, and between 15 and 18. If the patternPeriod is 20, there is also a gap between 18 and 20. + +### The DashDotLines rprim and shader +In HdStorm, we will add the HdDashDotLines rprim for the DashDotLines primitive. The topology of the DashDotLines requires the shape detail ("allDetails" or "noCapJoint"), curveVertexCounts, curveIndices and whether the pattern is screenspaced. When the shapeDetail is "noCapJoint", the primitive type is PRIM_BASIS_CURVES_LINES. And when the shapeDetail is "allDetails", we add a new primitive type: PRIM_DASH_DOT_LINES, and the lines will be implemented as a list of rectangles, and each rectangle will contain two Striangles. + +In dashDotLines.glslfx, we add four sections of shader code: "DashDotDefault.Vertex" and "DashDotDefault.Fragment" are for the "allDetails" implementation. "NoCapJoint.Vertex" and "NoCapJoint.Fragment" are for the "noCapJoint" implementation. + +Different from the other primitive, the dashDotLines should not have effects under lights. So we use a different fallback material. The dashDotFallbackMaterialNetwork.glslfx save the default material shader. The surface shader will just return the color the line, so that the lights will not influent the lines. + +### Other inputs for the shader and screen space pattern implementation +For a polyline, the shader need to know the sum of line lengths before each vertex. This value can be pre-calculated in CPU. To implement screen space dash-dot pattern, the sum must be based on line lengths on the screen. So to calculate the sum, we need to do matrix transformation for the lines in CPU, and this calculation must be done when camera is changed. (Maybe we can use the compute shader to do the calculation before the rendering process in each frame) + +# Examples +### 2 DashDotLines primitives with dash-dot patterns, and the shape detail is "allDetails" +``` +def DashDotLines "StyledPolyline1" ( + inherits = [] +){ + uniform token shapeDetail = "allDetails" + uniform bool screenspacePattern = true + uniform token startCapType = "round" + uniform token endCapType = "round" + uniform token jointType = "round" + float patternScale = 5 + int[] curveVertexCounts = [3, 4] + point3f[] points = [(0, 0, 0), (10, 10, 0), (10, 20, 0), (0, 30, 0), (-10, 40, 0), (-10, 50, 0), (0, 60, 0)] + float[] widths = [5] (interpolation = "constant") + color3f[] primvars:displayColor = [(1, 0, 0)] +} +def DashDotLines "StyledPolyline2" ( + inherits = [] +){ + uniform token shapeDetail = "allDetails" + uniform bool screenspacePattern = true + uniform token startCapType = "triangle" + uniform token endCapType = "triangle" + uniform token jointType = "round" + uniform float patternScale = 11 + int[] curveVertexCounts = [3, 4] + point3f[] points = [(20, 0, 0), (30, 10, 0), (30, 20, 0), (20, 30, 0), (10, 40, 0), (10, 50, 0), (20, 60, 0)] + float[] widths = [10] (interpolation = "constant") + color3f[] primvars:displayColor = [(0, 0, 1)] +} + +def "Pattern" ( + prepend apiSchemas = ["DashDotPatternAPI"] +) +{ + uniform float patternPeriod = 10 + uniform float2[] pattern = [(0, 5), (2.5, 0)] +} +``` +In this example, there are two DashDotLines primitives. They all inherits from the same dash-dot pattern, and the pattern is defined in "Pattern". The period of the pattern is 10. There are two symbols in the pattern. The first symbol starts at 0, and it is a dash with length 5. The second symbol starts at 7.5, and it is a dot. + +The first primitive has two polylines. One polyline has 3 vertices and another has 4 vertices. The line width on screen is 5. The startCapType and endCapType are both round. The patternScale is 5 which means the dashes and gaps will be lengthened by 5 times. + +The second primitive has two polylines. One polyline has 3 vertices and another has 4 vertices. The line width on screen is 10. The startCapType and endCapType are both triangle. The patternScale is 11 which means the dashes and gaps will be lengthened by 11 times. + +The image for the 2 DashDotLines primitives. + +![image of Dashdotlines primitives](twoPolylines.png) + +### 2 DashDotLines primitives with dash-dot patterns, and the shape detail is "noCapJoint" +``` +def DashDotLines "StyledPolyline1" ( + inherits = [] +){ + uniform token shapeDetail = "noCapJoint" + uniform bool screenspacePattern = true + uniform token startCapType = "round" + uniform token endCapType = "round" + uniform token jointType = "round" + float patternScale = 5 + int[] curveVertexCounts = [3, 4] + point3f[] points = [(0, 0, 0), (10, 10, 0), (10, 20, 0), (0, 30, 0), (-10, 40, 0), (-10, 50, 0), (0, 60, 0)] + float[] widths = [5] (interpolation = "constant") + color3f[] primvars:displayColor = [(1, 0, 0)] +} +def DashDotLines "StyledPolyline2" ( + inherits = [] +){ + uniform token shapeDetail = "noCapJoint" + uniform bool screenspacePattern = true + uniform token startCapType = "triangle" + uniform token endCapType = "triangle" + uniform token jointType = "round" + uniform float patternScale = 11 + int[] curveVertexCounts = [3, 4] + point3f[] points = [(20, 0, 0), (30, 10, 0), (30, 20, 0), (20, 30, 0), (10, 40, 0), (10, 50, 0), (20, 60, 0)] + float[] widths = [10] (interpolation = "constant") + color3f[] primvars:displayColor = [(0, 0, 1)] +} + +def "Pattern" ( + prepend apiSchemas = ["DashDotPatternAPI"] +) +{ + uniform float patternPeriod = 10 + uniform float2[] pattern = [(0, 5), (2.5, 0)] +} +``` +In this example, the two DashDotLines primitive are similar as the above example. The only difference is that the shapeDetail is "noCapJoint". + +The image for the 2 DashDotLines primitives. + +![image of Dashdotlines primitives](twoPolylinesNoCapJoint.png) + +### A polyline with no pattern +``` +def DashDotLines "StyledPolyline" ( +){ + uniform token shapeDetail = "allDetails" + uniform token startCapType = "round" + uniform token endCapType = "round" + uniform token jointType = "round" + int[] curveVertexCounts = [3] + point3f[] points = [(0, 0, 0), (10, 10, 0), (10, 20, 0)] + float[] widths = [5] (interpolation = "constant") + color3f[] primvars:displayColor = [(1, 0, 0)] +} +``` +In this example, there is one polyline. It has 3 vertices. The line width on screen is 5. The polyline doesn't have pattern. The startCapType and endCapType are both round. + +The image for the DashDotLines primitive. + +![image of Dashdotlines primitive](onePolyline.png) \ No newline at end of file diff --git a/proposals/_notPublished/hold/LineStyle/onePolyline.png b/proposals/_notPublished/hold/LineStyle/onePolyline.png new file mode 100644 index 00000000..03c45a04 Binary files /dev/null and b/proposals/_notPublished/hold/LineStyle/onePolyline.png differ diff --git a/proposals/_notPublished/hold/LineStyle/twoPolylines.png b/proposals/_notPublished/hold/LineStyle/twoPolylines.png new file mode 100644 index 00000000..a5294f09 Binary files /dev/null and b/proposals/_notPublished/hold/LineStyle/twoPolylines.png differ diff --git a/proposals/_notPublished/hold/LineStyle/twoPolylinesNoCapJoint.png b/proposals/_notPublished/hold/LineStyle/twoPolylinesNoCapJoint.png new file mode 100644 index 00000000..762baae3 Binary files /dev/null and b/proposals/_notPublished/hold/LineStyle/twoPolylinesNoCapJoint.png differ