Browse Source

MeshTools: add generateQuadIndices().

pull/470/head
Vladimír Vondruš 6 years ago
parent
commit
b77eafb9de
  1. 5
      doc/changelog.dox
  2. 9
      doc/snippets/README.md
  3. 24
      doc/snippets/triangulate.svg
  4. 212
      doc/triangulate.svg
  5. 101
      src/Magnum/MeshTools/GenerateIndices.cpp
  6. 63
      src/Magnum/MeshTools/GenerateIndices.h
  7. 226
      src/Magnum/MeshTools/Test/GenerateIndicesTest.cpp

5
doc/changelog.dox

@ -68,6 +68,11 @@ See also:
- Added @ref Math::fmod() (see [mosra/magnum#454](https://github.com/mosra/magnum/pull/454))
- Added @ref Math::binomialCoefficient() (see [mosra/magnum#461](https://github.com/mosra/magnum/pull/461))
@subsubsection changelog-latest-new-meshtools MeshTools library
- Added @ref MeshTools::generateQuadIndices() for quad triangulation
including non-convex and non-planar quads
@subsubsection changelog-latest-new-scenegraph SceneGraph library
- Added @ref SceneGraph::Object::move()

9
doc/snippets/README.md

@ -13,3 +13,12 @@ smaller file sizes:
The output printed by the application can be used to update the example output
in `doc/getting-started.dox`.
### triangulate.svg
Created by Inkscape from `doc/triangulate.svg` by saving as Optimized SVG and:
- cleaning up the `<svg>` header
- converting to a `style=""`, *keeping* `viewBox`
- adding `class="m-image"`
- removing metadata and the background layer

24
doc/snippets/triangulate.svg

@ -0,0 +1,24 @@
<svg class="m-image" style="width: 436.64px; height: 130.13px" viewBox="0 0 115.53 34.429">
<g transform="translate(-2.3651 -68.775)">
<g fill="none">
<path d="m9.6784 85.99h28.835" stroke="#2f83cc" stroke-width=".52917"/>
<path d="m48.969 76.266v19.447" stroke="#747474" stroke-dasharray="0.264583, 0.529167" stroke-linejoin="round" stroke-width=".26458"/>
<path d="m48.969 76.266-39.291 9.7234 39.291 9.7234-10.455-9.7234z" stroke="#dcdcdc" stroke-linecap="round" stroke-linejoin="round" stroke-width=".26458"/>
</g>
<g fill="#dcdcdc" font-family="'Source Sans Pro'" font-size="4.2333px" stroke-width=".26458">
<text transform="translate(0 -.34713)" x="49.513424" y="100.9054" dominant-baseline="auto" stop-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;font-variation-settings:normal;inline-size:0;line-height:1.25;shape-margin:0;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal" xml:space="preserve"><tspan x="49.513424" y="100.9054" dominant-baseline="auto" fill="#dcdcdc" font-family="'Source Sans Pro'" font-size="4.2333px" stop-color="#000000" stroke-width=".26458" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;font-variation-settings:normal;inline-size:0;line-height:1.25;shape-margin:0;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal">B</tspan></text>
<text transform="translate(0 -.34714)" x="4.9981942" y="87.725258" dominant-baseline="auto" stop-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;font-variation-settings:normal;inline-size:0;line-height:1.25;shape-margin:0;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal" xml:space="preserve"><tspan x="4.9981942" y="87.725258" dominant-baseline="auto" fill="#dcdcdc" font-family="'Source Sans Pro'" font-size="4.2333px" stop-color="#000000" stroke-width=".26458" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;font-variation-settings:normal;inline-size:0;line-height:1.25;shape-margin:0;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal">A</tspan></text>
<text transform="translate(0 -.34714)" x="42.423912" y="87.725258" dominant-baseline="auto" stop-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;font-variation-settings:normal;inline-size:0;line-height:1.25;shape-margin:0;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal" xml:space="preserve"><tspan x="42.423912" y="87.725258" dominant-baseline="auto" fill="#dcdcdc" font-family="'Source Sans Pro'" font-size="4.2333px" stop-color="#000000" stroke-width=".26458" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;font-variation-settings:normal;inline-size:0;line-height:1.25;shape-margin:0;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal">C</tspan></text>
</g>
<rect transform="matrix(-.89443 .44721 -.89443 -.44721 0 0)" x="34.029" y="-158.38" width="21.746" height="21.998" fill="none" stop-color="#000000" stroke="#dcdcdc" stroke-linecap="round" stroke-linejoin="round" stroke-width=".29581"/>
<g fill="#dcdcdc" font-family="'Source Sans Pro'" font-size="4.2333px" stroke-width=".26458">
<text transform="translate(0 -.34714)" x="67.480515" y="87.725258" dominant-baseline="auto" stop-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;font-variation-settings:normal;inline-size:0;line-height:1.25;shape-margin:0;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal" xml:space="preserve"><tspan x="67.480515" y="87.725258" dominant-baseline="auto" fill="#dcdcdc" font-family="'Source Sans Pro'" font-size="4.2333px" stop-color="#000000" stroke-width=".26458" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;font-variation-settings:normal;inline-size:0;line-height:1.25;shape-margin:0;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal">A</tspan></text>
<text transform="translate(0 -.34714)" x="112.96389" y="87.725258" dominant-baseline="auto" stop-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;font-variation-settings:normal;inline-size:0;line-height:1.25;shape-margin:0;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal" xml:space="preserve"><tspan x="112.96389" y="87.725258" dominant-baseline="auto" fill="#dcdcdc" font-family="'Source Sans Pro'" font-size="4.2333px" stop-color="#000000" stroke-width=".26458" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;font-variation-settings:normal;inline-size:0;line-height:1.25;shape-margin:0;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal">C</tspan></text>
<text transform="translate(.34608 -.34713)" x="89.925873" y="74.545097" dominant-baseline="auto" stop-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;font-variation-settings:normal;inline-size:0;line-height:1.25;shape-margin:0;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal" xml:space="preserve"><tspan x="89.925873" y="74.545097" dominant-baseline="auto" fill="#dcdcdc" font-family="'Source Sans Pro'" font-size="4.2333px" stop-color="#000000" stroke-width=".26458" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;font-variation-settings:normal;inline-size:0;line-height:1.25;shape-margin:0;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal">D</tspan></text>
<text transform="translate(.34608 -.34713)" x="89.968216" y="100.9054" dominant-baseline="auto" stop-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;font-variation-settings:normal;inline-size:0;line-height:1.25;shape-margin:0;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal" xml:space="preserve"><tspan x="89.968216" y="100.9054" dominant-baseline="auto" fill="#dcdcdc" font-family="'Source Sans Pro'" font-size="4.2333px" stop-color="#000000" stroke-width=".26458" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;font-variation-settings:normal;inline-size:0;line-height:1.25;shape-margin:0;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal">B</tspan></text>
</g>
<path d="m91.543 76.208 0.22559 19.563" fill="none" stroke="#2f83cc" stroke-width=".52917"/>
<path d="m111.22 86.046-39.126-0.1128" fill="none" stroke="#747474" stroke-dasharray="0.264583, 0.529167" stroke-linejoin="round" stroke-width=".26458"/>
<text x="49.513424" y="74.197968" fill="#dcdcdc" font-family="'Source Sans Pro'" font-size="4.2333px" stroke-width=".26458" style="font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="49.513424" y="74.197968" fill="#dcdcdc" font-family="'Source Sans Pro'" font-size="4.2333px" stroke-width=".26458" style="font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal">D</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

212
doc/triangulate.svg

@ -0,0 +1,212 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
sodipodi:docname="triangulate.svg"
id="svg8"
version="1.1"
viewBox="0 0 115.52642 34.42904"
height="130.1255"
width="436.63531">
<defs
id="defs2" />
<sodipodi:namedview
inkscape:window-maximized="0"
inkscape:window-y="44"
inkscape:window-x="449"
inkscape:window-height="896"
inkscape:window-width="1280"
inkscape:snap-page="true"
inkscape:snap-nodes="true"
fit-margin-bottom="10"
fit-margin-right="10"
fit-margin-left="10"
lock-margins="true"
fit-margin-top="10"
units="px"
inkscape:snap-global="true"
inkscape:snap-midpoints="true"
inkscape:bbox-nodes="false"
inkscape:object-nodes="true"
inkscape:snap-bbox-edge-midpoints="false"
inkscape:snap-object-midpoints="true"
inkscape:snap-bbox-midpoints="false"
inkscape:snap-bbox="true"
showgrid="false"
inkscape:document-rotation="0"
inkscape:current-layer="layer2"
inkscape:document-units="mm"
inkscape:cy="191.94229"
inkscape:cx="167.77738"
inkscape:zoom="1"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
style="display:none"
transform="translate(-2.3650609,-68.77507)"
inkscape:label="bg"
id="layer2"
inkscape:groupmode="layer">
<rect
y="68.77507"
x="2.3650608"
height="34.429039"
width="115.52643"
id="rect845"
style="fill:#2f363f;fill-opacity:1;stroke:none;stroke-width:0.264582;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;stop-color:#000000" />
</g>
<g
transform="translate(-2.3650609,-68.77507)"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Vrstva 1">
<path
id="path841"
d="M 9.6783945,85.989587 H 38.513695"
style="fill:none;stroke:#2f83cc;stroke-width:0.529167;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path843"
d="M 48.969022,76.266149 V 95.713023"
style="fill:none;stroke:#747474;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:0.264583, 0.529167;stroke-dashoffset:0;stroke-opacity:1" />
<path
sodipodi:nodetypes="ccccc"
id="path839"
d="M 48.969022,76.266149 9.6783945,85.989587 48.969022,95.713023 38.513698,85.989587 Z"
style="fill:none;stroke:#dcdcdc;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<text
transform="translate(0,-0.34712998)"
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;line-height:1.25;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;opacity:1;vector-effect:none;fill:#dcdcdc;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
x="49.513424"
y="100.9054"
id="text865"><tspan
sodipodi:role="line"
id="tspan863"
x="49.513424"
y="100.9054"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;line-height:1.25;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#dcdcdc;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1">B</tspan></text>
<text
transform="translate(0,-0.34713871)"
id="text851"
y="87.725258"
x="4.9981942"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;line-height:1.25;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;opacity:1;vector-effect:none;fill:#dcdcdc;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;line-height:1.25;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#dcdcdc;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
y="87.725258"
x="4.9981942"
id="tspan849"
sodipodi:role="line">A</tspan></text>
<text
transform="translate(0,-0.34713871)"
id="text883"
y="87.725258"
x="42.423912"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;line-height:1.25;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;opacity:1;vector-effect:none;fill:#dcdcdc;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;line-height:1.25;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#dcdcdc;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
y="87.725258"
x="42.423912"
id="tspan881"
sodipodi:role="line">C</tspan></text>
<rect
transform="matrix(-0.89442745,0.44721307,-0.89442745,-0.44721307,0,0)"
y="-158.37596"
x="34.028938"
height="21.998369"
width="21.746136"
id="rect885"
style="fill:none;fill-opacity:1;stroke:#dcdcdc;stroke-width:0.295814;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000" />
<text
transform="translate(0,-0.34713871)"
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;line-height:1.25;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;opacity:1;vector-effect:none;fill:#dcdcdc;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
x="67.480515"
y="87.725258"
id="text889"><tspan
sodipodi:role="line"
id="tspan887"
x="67.480515"
y="87.725258"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;line-height:1.25;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#dcdcdc;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1">A</tspan></text>
<text
transform="translate(0,-0.34713871)"
id="text893"
y="87.725258"
x="112.96389"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;line-height:1.25;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;opacity:1;vector-effect:none;fill:#dcdcdc;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;line-height:1.25;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#dcdcdc;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
y="87.725258"
x="112.96389"
id="tspan891"
sodipodi:role="line">C</tspan></text>
<text
transform="translate(0.34608236,-0.34712858)"
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;line-height:1.25;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;opacity:1;vector-effect:none;fill:#dcdcdc;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
x="89.925873"
y="74.545097"
id="text897"><tspan
sodipodi:role="line"
id="tspan895"
x="89.925873"
y="74.545097"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;line-height:1.25;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#dcdcdc;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1">D</tspan></text>
<text
transform="translate(0.34608236,-0.34712858)"
id="text901"
y="100.9054"
x="89.968216"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;line-height:1.25;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;opacity:1;vector-effect:none;fill:#dcdcdc;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
xml:space="preserve"><tspan
id="tspan907"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;line-height:1.25;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#dcdcdc;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
y="100.9054"
x="89.968216"
sodipodi:role="line">B</tspan></text>
<path
sodipodi:nodetypes="cc"
style="fill:none;stroke:#2f83cc;stroke-width:0.529167;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 91.543457,76.208034 0.225594,19.563111"
id="path903" />
<path
sodipodi:nodetypes="cc"
style="fill:none;stroke:#747474;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:0.264583, 0.529167;stroke-dashoffset:0;stroke-opacity:1"
d="m 111.21939,86.045988 -39.126276,-0.1128"
id="path905" />
<text
id="text869"
y="74.197968"
x="49.513424"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;line-height:1.25;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#dcdcdc;fill-opacity:1;stroke:none;stroke-width:0.264583"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#dcdcdc;fill-opacity:1;stroke-width:0.264583"
y="74.197968"
x="49.513424"
id="tspan867"
sodipodi:role="line">D</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 22 KiB

101
src/Magnum/MeshTools/GenerateIndices.cpp

@ -29,6 +29,7 @@
#include <Corrade/Containers/StridedArrayView.h>
#include <Corrade/Utility/Algorithms.h>
#include "Magnum/Math/Vector3.h"
#include "Magnum/Trade/MeshData.h"
namespace Magnum { namespace MeshTools {
@ -168,6 +169,106 @@ Containers::Array<UnsignedInt> generateTriangleFanIndices(const UnsignedInt vert
return indices;
}
namespace {
template<class T> inline void generateQuadIndicesIntoImplementation(const Containers::StridedArrayView1D<const Vector3>& positions, const Containers::StridedArrayView1D<const T>& quads, const Containers::StridedArrayView1D<T>& into) {
CORRADE_ASSERT(quads.size() % 4 == 0,
"MeshTools::generateQuadIndicesInto(): quad index count" << quads.size() << "not divisible by 4", );
CORRADE_ASSERT(quads.size()*6/4 == into.size(),
"MeshTools::generateQuadIndicesInto(): bad output size, expected" << quads.size()*6/4 << "but got" << into.size(), );
for(std::size_t i = 0, max = quads.size()/4; i != max; ++i) {
auto get = [&](UnsignedInt j) -> const Vector3& {
UnsignedInt index = quads[4*i + j];
CORRADE_ASSERT(index < positions.size(),
"MeshTools::generateQuadIndicesInto(): index" << index << "out of bounds for" << positions.size() << "elements", positions[0]);
return positions[index];
};
const Vector3& a = get(0);
const Vector3& b = get(1);
const Vector3& c = get(2);
const Vector3& d = get(3);
constexpr UnsignedInt SplitAbcAcd[] { 0, 1, 2, 0, 2, 3 };
constexpr UnsignedInt SplitDabDbc[] { 3, 0, 1, 3, 1, 2 };
const UnsignedInt* split;
const bool abcAcdOppositeDirection = Math::dot(Math::cross(c - b, a - b), Math::cross(d - c, a - c)) < 0.0f;
const bool dabDbcOppositeDirection = Math::dot(Math::cross(d - b, a - b), Math::cross(c - b, d - b)) < 0.0f;
/* If normals of ABC and ACD point in opposite direction and DAB DBC
point in the same direction, split as DAB DBC; and vice versa. */
if(abcAcdOppositeDirection != dabDbcOppositeDirection)
split = abcAcdOppositeDirection ? SplitDabDbc : SplitAbcAcd;
/* Otherwise the normals of both cases point in the same direction or
it's a pathological case where both cases point in the opposite.
Pick the shorter diagonal. If both are the same, pick the "obvious"
ABC ACD. */
else split = (b - d).dot() < (c - a).dot() ? SplitDabDbc : SplitAbcAcd;
/* Assign the two triangles */
for(std::size_t j = 0; j != 6; ++j)
into[6*i + j] = quads[4*i + split[j]];
}
}
}
Containers::Array<UnsignedInt> generateQuadIndices(const Containers::StridedArrayView1D<const Vector3>& positions, const Containers::StridedArrayView1D<const UnsignedInt>& quads) {
/* We can skip zero-initialization here */
Containers::Array<UnsignedInt> out{Containers::NoInit, quads.size()*6/4};
generateQuadIndicesIntoImplementation(positions, quads, Containers::stridedArrayView(out));
return out;
}
Containers::Array<UnsignedInt> generateQuadIndices(const Containers::StridedArrayView1D<const Vector3>& positions, const Containers::StridedArrayView1D<const UnsignedShort>& quads) {
/* Explicitly ensure we have the unused bytes zeroed out */
Containers::Array<UnsignedInt> out{Containers::ValueInit, quads.size()*6/4};
generateQuadIndicesIntoImplementation(positions, quads,
/* Could be just arrayCast<UnsignedShort>(stridedArrayView(out) on LE,
but I want to be sure as much as possible that this compiles on BE
as well. Hmm, now I get why LE won. */
Containers::StridedArrayView1D<UnsignedShort>{
Containers::arrayCast<UnsignedShort>(out),
reinterpret_cast<UnsignedShort*>(out.data())
#ifdef CORRADE_BIG_ENDIAN
+ 1
#endif
, out.size(), 4}
);
return out;
}
Containers::Array<UnsignedInt> generateQuadIndices(const Containers::StridedArrayView1D<const Vector3>& positions, const Containers::StridedArrayView1D<const UnsignedByte>& quads) {
/* Explicitly ensure we have the unused bytes zeroed out */
Containers::Array<UnsignedInt> out{Containers::ValueInit, quads.size()*6/4};
generateQuadIndicesIntoImplementation(positions, quads,
/* Could be just arrayCast<UnsignedShort>(stridedArrayView(out) on LE,
but I want to be sure as much as possible that this compiles on BE
as well. Hmm, now I get why LE won. */
Containers::StridedArrayView1D<UnsignedByte>{
Containers::arrayCast<UnsignedByte>(out),
reinterpret_cast<UnsignedByte*>(out.data())
#ifdef CORRADE_BIG_ENDIAN
+ 3
#endif
, out.size(), 4}
);
return out;
}
void generateQuadIndicesInto(const Containers::StridedArrayView1D<const Vector3>& positions, const Containers::StridedArrayView1D<const UnsignedInt>& quads, const Containers::StridedArrayView1D<UnsignedInt>& into) {
return generateQuadIndicesIntoImplementation(positions, quads, into);
}
void generateQuadIndicesInto(const Containers::StridedArrayView1D<const Vector3>& positions, const Containers::StridedArrayView1D<const UnsignedShort>& quads, const Containers::StridedArrayView1D<UnsignedShort>& into) {
return generateQuadIndicesIntoImplementation(positions, quads, into);
}
void generateQuadIndicesInto(const Containers::StridedArrayView1D<const Vector3>& positions, const Containers::StridedArrayView1D<const UnsignedByte>& quads, const Containers::StridedArrayView1D<UnsignedByte>& into) {
return generateQuadIndicesIntoImplementation(positions, quads, into);
}
Trade::MeshData generateIndices(Trade::MeshData&& data) {
CORRADE_ASSERT(!data.isIndexed(),
"MeshTools::generateIndices(): mesh data already indexed",

63
src/Magnum/MeshTools/GenerateIndices.h

@ -144,6 +144,69 @@ least @cpp 3 @ce, the @p indices array is expected to have a size of
*/
MAGNUM_MESHTOOLS_EXPORT void generateTriangleFanIndicesInto(UnsignedInt vertexCount, const Containers::StridedArrayView1D<UnsignedInt>& into);
/**
@brief Create a triangle index buffer for quad primitives
@m_since_latest
@htmlinclude triangulate.svg
For each quad `ABCD` gives a pair of triangles that is either `ABC ACD` or
`DAB DBC`, correctly handling cases of non-convex quads and avoiding thin
triangles where possible. Loosely based on [this SO question](https://stackoverflow.com/q/12239876):
1. If normals of triangles `ABC` and `ACD` point in opposite direction and
`DAB DBC` not (which is equivalent to points `D` and `B` being on the same
side of a diagonal `AC` in a two-dimensional case), split as `DAB DBC`
2. Otherwise, if normals of triangles `DAB` and `DBC` point in opposite
direction and `ABC ACD` not (which is equivalent to points `A` and `C`
being on the same side of a diagonal `DB` in a two-dimensional case), split
as `ABC ACD`
3. Otherwise the normals either point in the same direction in both cases or
the quad is non-planar and ambiguous, pick the case where the diagonal is
shorter
Size of @p quads is expected to be divisible by @cpp 4 @ce and all indices
being in bounds of the @p positions view.
@see @ref generateQuadIndicesInto(), \n
@ref Math::cross(const Vector3<T>&, const Vector3<T>&), \n
@ref Math::dot(const Vector<size, T>&, const Vector<size, T>&)
*/
MAGNUM_MESHTOOLS_EXPORT Containers::Array<UnsignedInt> generateQuadIndices(const Containers::StridedArrayView1D<const Vector3>& positions, const Containers::StridedArrayView1D<const UnsignedInt>& quads);
/**
* @overload
* @m_since_latest
*/
MAGNUM_MESHTOOLS_EXPORT Containers::Array<UnsignedInt> generateQuadIndices(const Containers::StridedArrayView1D<const Vector3>& positions, const Containers::StridedArrayView1D<const UnsignedShort>& quads);
/**
* @overload
* @m_since_latest
*/
MAGNUM_MESHTOOLS_EXPORT Containers::Array<UnsignedInt> generateQuadIndices(const Containers::StridedArrayView1D<const Vector3>& positions, const Containers::StridedArrayView1D<const UnsignedByte>& quads);
/**
@brief Create a triangle index buffer for quad primitives into an existing array
@m_since_latest
A variant of @ref generateQuadIndices() that fills existing memory instead of
allocating a new array. Size of @p quads is expected to be divisible by @cpp 4 @ce
and @p into should have a size that's @cpp quads.size()*6/4 @ce.
*/
MAGNUM_MESHTOOLS_EXPORT void generateQuadIndicesInto(const Containers::StridedArrayView1D<const Vector3>& positions, const Containers::StridedArrayView1D<const UnsignedInt>& quads, const Containers::StridedArrayView1D<UnsignedInt>& into);
/**
* @overload
* @m_since_latest
*/
MAGNUM_MESHTOOLS_EXPORT void generateQuadIndicesInto(const Containers::StridedArrayView1D<const Vector3>& positions, const Containers::StridedArrayView1D<const UnsignedShort>& quads, const Containers::StridedArrayView1D<UnsignedShort>& into);
/**
* @overload
* @m_since_latest
*/
MAGNUM_MESHTOOLS_EXPORT void generateQuadIndicesInto(const Containers::StridedArrayView1D<const Vector3>& positions, const Containers::StridedArrayView1D<const UnsignedByte>& quads, const Containers::StridedArrayView1D<UnsignedByte>& into);
/**
@brief Convert a mesh to plain indexed lines or triangles
@m_since{2020,06}

226
src/Magnum/MeshTools/Test/GenerateIndicesTest.cpp

@ -30,7 +30,7 @@
#include <Corrade/TestSuite/Compare/Container.h>
#include <Corrade/Utility/DebugStl.h>
#include "Magnum/Math/Vector2.h"
#include "Magnum/Math/Matrix4.h"
#include "Magnum/MeshTools/GenerateIndices.h"
#include "Magnum/Trade/MeshData.h"
@ -58,12 +58,77 @@ struct GenerateIndicesTest: TestSuite::Tester {
void generateTriangleFanIndicesWrongVertexCount();
void generateTriangleFanIndicesIntoWrongSize();
template<class T> void generateQuadIndices();
template<class T> void generateQuadIndicesInto();
void generateQuadIndicesWrongIndexCount();
void generateQuadIndicesIndexOutOfBounds();
void generateQuadIndicesIntoWrongSize();
void generateIndicesMeshData();
void generateIndicesMeshDataMove();
void generateIndicesMeshDataIndexed();
void generateIndicesMeshDataInvalidPrimitive();
};
using namespace Math::Literals;
const struct {
const char* name;
Matrix4 transformation;
UnsignedInt remap[4];
UnsignedInt expected[6*5];
} QuadData[] {
{"", {}, {0, 1, 2, 3}, {
0, 2, 3, 0, 3, 4, // ABC ACD
9, 5, 6, 9, 6, 7, // DAB DBC
10, 11, 14, 10, 14, 15, // ABC ACD
19, 16, 17, 19, 17, 18, // DAB DBC
20, 21, 22, 20, 22, 23 // ABC ACD
}},
{"rotated indices 1", {}, {1, 2, 3, 0}, {
2, 3, 4, 2, 4, 0, // BCD BDA (both splits are fine)
6, 7, 9, 6, 9, 5, // BCD BDA
10, 11, 14, 10, 14, 15, // ABC ACD
17, 18, 19, 17, 19, 16, // BCD BDA
20, 21, 22, 20, 22, 23 // ABC ACD
}},
{"rotated indices 2", {}, {2, 3, 0, 1}, {
3, 4, 0, 3, 0, 2, // CDA CAB
6, 7, 9, 6, 9, 5, // BCD BDA
14, 15, 10, 14, 10, 11, // CDA CAB
17, 18, 19, 17, 19, 16, // BCD BDA
22, 23, 20, 22, 20, 21 // CDA CAB
}},
{"rotated indices 3", {}, {3, 0, 1, 2}, {
4, 0, 2, 4, 2, 3, // DAB DBC (both splits are fine)
9, 5, 6, 9, 6, 7, // DAB DBC
14, 15, 10, 14, 10, 11, // CDA CAB
19, 16, 17, 19, 17, 18, // DAB DBC
22, 23, 20, 22, 20, 21 // CDA CAB
}},
{"reversed indices", {}, {3, 2, 1, 0}, {
4, 3, 2, 4, 2, 0, // DCB DBA (both splits are fine)
9, 7, 6, 9, 6, 5, // DCB DBA
10, 15, 14, 10, 14, 11, // ADC ACB
19, 18, 17, 19, 17, 16, // DCB DBA
20, 23, 22, 20, 22, 21 // ADC ACB
}},
{"rotated positions", Matrix4::rotation(130.0_degf, Vector3{1.0f/Constants::sqrt3()}), {0, 1, 2, 3}, {
0, 2, 3, 0, 3, 4, // ABC ACD
9, 5, 6, 9, 6, 7, // DAB DBC
10, 11, 14, 10, 14, 15, // ABC ACD
19, 16, 17, 19, 17, 18, // DAB DBC
20, 21, 22, 20, 22, 23 // ABC ACD
}},
{"mirrored positions", Matrix4::scaling(Vector3::xScale(-1.0f)), {0, 1, 2, 3}, {
0, 2, 3, 0, 3, 4, // ABC ACD
9, 5, 6, 9, 6, 7, // DAB DBC
10, 11, 14, 10, 14, 15, // ABC ACD
19, 16, 17, 19, 17, 18, // DAB DBC
20, 21, 22, 20, 22, 23 // ABC ACD
}}
};
const struct {
MeshPrimitive primitive;
Containers::Array<UnsignedInt> indices;
@ -113,6 +178,19 @@ GenerateIndicesTest::GenerateIndicesTest() {
&GenerateIndicesTest::generateTriangleFanIndicesWrongVertexCount,
&GenerateIndicesTest::generateTriangleFanIndicesIntoWrongSize});
addInstancedTests<GenerateIndicesTest>({
&GenerateIndicesTest::generateQuadIndices<UnsignedInt>,
&GenerateIndicesTest::generateQuadIndices<UnsignedShort>,
&GenerateIndicesTest::generateQuadIndices<UnsignedByte>},
Containers::arraySize(QuadData));
addTests({&GenerateIndicesTest::generateQuadIndicesInto<UnsignedInt>,
&GenerateIndicesTest::generateQuadIndicesInto<UnsignedShort>,
&GenerateIndicesTest::generateQuadIndicesInto<UnsignedByte>,
&GenerateIndicesTest::generateQuadIndicesWrongIndexCount,
&GenerateIndicesTest::generateQuadIndicesIndexOutOfBounds,
&GenerateIndicesTest::generateQuadIndicesIntoWrongSize});
addInstancedTests({&GenerateIndicesTest::generateIndicesMeshData},
Containers::arraySize(MeshDataData));
@ -379,6 +457,152 @@ void GenerateIndicesTest::generateTriangleFanIndicesIntoWrongSize() {
"MeshTools::generateTriangleFanIndicesInto(): bad output size, expected 9 but got 8\n");
}
constexpr Vector3 QuadPositions[] {
/*
D C
-> ABC ACD (trivial case)
A B
*/
{0.0f, 0.0f, 0.0f}, {}, // 0
{1.0f, 0.0f, 0.0f}, // 2
{1.0f, 1.0f, 0.0f}, // 3
{0.0f, 1.0f, 0.0f}, // 4
/*
D
A C -> DAB DBC (shorter diagonal)
B
*/
{ 0.0f, 0.0f, 1.0f}, // 5
{ 5.0f, 0.0f, 0.0f}, // 6
{10.0f, 0.0f, 1.0f}, {}, // 7
{ 5.0f, 0.0f, 2.0f}, // 9
/*
D
A C -> ABC ACD (concave)
B
*/
{0.0f, 0.5f, 0.0f}, // 10
{5.0f, 0.0f, 0.0f}, {}, {}, // 11
{4.0f, 0.5f, 0.0f}, // 14
{5.0f, 1.0f, 0.0f}, // 15
/*
C
D B -> DAB DBC (concave, non-planar)
A
*/
{5.0f, 0.0f, 0.5f}, // 16
{4.0f, 0.5f, 1.0f}, // 17
{5.0f, 1.0f, 0.5f}, // 18
{0.0f, 0.5f, 1.0f}, // 19
/*
C
D B -> ABC ACD (concave, non-planar, ambiguous -> picking
A shorter diagonal)
*/
{5.0f, 0.0f, 0.5f}, // 20
{4.0f, 0.5f, 2.0f}, // 21
{5.0f, 1.0f, 0.5f}, // 22
{0.0f, 0.5f, 1.0f}, // 23
};
constexpr UnsignedInt QuadIndices[] {
0, 2, 3, 4,
5, 6, 7, 9,
10, 11, 14, 15,
16, 17, 18, 19,
20, 21, 22, 23
};
template<class T> void GenerateIndicesTest::generateQuadIndices() {
auto&& data = QuadData[testCaseInstanceId()];
setTestCaseTemplateName(Math::TypeTraits<T>::name());
setTestCaseDescription(data.name);
Vector3 transformedPositions[Containers::arraySize(QuadPositions)];
for(std::size_t i = 0; i != Containers::arraySize(QuadPositions); ++i)
transformedPositions[i] = data.transformation.transformPoint(QuadPositions[i]);
T remappedIndices[Containers::arraySize(QuadIndices)];
for(std::size_t i = 0; i != Containers::arraySize(QuadIndices)/4; ++i)
for(std::size_t j = 0; j != 4; ++j)
remappedIndices[i*4 + j] = QuadIndices[i*4 + data.remap[j]];
Containers::Array<UnsignedInt> triangleIndices =
MeshTools::generateQuadIndices(transformedPositions, remappedIndices);
CORRADE_COMPARE_AS(Containers::arrayView(triangleIndices), Containers::arrayView(data.expected), TestSuite::Compare::Container);
}
template<class T> void GenerateIndicesTest::generateQuadIndicesInto() {
setTestCaseTemplateName(Math::TypeTraits<T>::name());
/* Simpler variant of the above w/o data transformations just to verify
everything is passed through as expected */
T indices[Containers::arraySize(QuadIndices)];
for(std::size_t i = 0; i != Containers::arraySize(QuadIndices); ++i)
indices[i] = QuadIndices[i];
T triangleIndices[Containers::arraySize(QuadIndices)*6/4];
MeshTools::generateQuadIndicesInto(QuadPositions, indices, triangleIndices);
CORRADE_COMPARE_AS(Containers::arrayView(triangleIndices), Containers::arrayView<T>({
0, 2, 3, 0, 3, 4, // ABC ACD
9, 5, 6, 9, 6, 7, // DAB DBC
10, 11, 14, 10, 14, 15, // ABC ACD
19, 16, 17, 19, 17, 18, // DAB DBC
20, 21, 22, 20, 22, 23 // ABC ACD
}), TestSuite::Compare::Container);
}
void GenerateIndicesTest::generateQuadIndicesWrongIndexCount() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
UnsignedInt quads[13];
std::ostringstream out;
Error redirectError{&out};
MeshTools::generateQuadIndices({}, quads);
CORRADE_COMPARE(out.str(),
"MeshTools::generateQuadIndicesInto(): quad index count 13 not divisible by 4\n");
}
void GenerateIndicesTest::generateQuadIndicesIndexOutOfBounds() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
UnsignedInt quads[]{5, 4, 6, 7};
Vector3 positions[7];
std::ostringstream out;
Error redirectError{&out};
MeshTools::generateQuadIndices(positions, quads);
CORRADE_COMPARE(out.str(),
"MeshTools::generateQuadIndicesInto(): index 7 out of bounds for 7 elements\n");
}
void GenerateIndicesTest::generateQuadIndicesIntoWrongSize() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
UnsignedInt quads[12];
UnsignedInt output[19];
std::ostringstream out;
Error redirectError{&out};
MeshTools::generateQuadIndicesInto({}, quads, output);
CORRADE_COMPARE(out.str(),
"MeshTools::generateQuadIndicesInto(): bad output size, expected 18 but got 19\n");
}
void GenerateIndicesTest::generateIndicesMeshData() {
auto&& data = MeshDataData[testCaseInstanceId()];
{

Loading…
Cancel
Save