Support importing another Dust3D document as a part

- Add example model: Backpacker
    To demonstrate the importing function
- Add menu: Reverse
    Reverse selected edges. The direction of edge decide the base normal of
    generated mesh.
- Refactor stroke mesh builder
    Remove recursive calls, more consistant generated result
- Add menu: Import
master
huxingyi 2020-04-08 08:45:20 +09:30
parent cb192bdaa6
commit 63cbfa83a5
74 changed files with 4128 additions and 2195 deletions

View File

@ -4,6 +4,10 @@ Changes between 1.0.0-rc.3 and 1.0.0-rc.4:
- Add menu: Export as Image - Add menu: Export as Image
- Add auto saving - Add auto saving
- Change to single instance with multiple windows - Change to single instance with multiple windows
- Refactor stroke mesh builder
- Add menu: Import
- Show edge direction on canvas
- Add example model: Backpacker
Changes between 1.0.0-rc.1 and 1.0.0-rc.3: Changes between 1.0.0-rc.1 and 1.0.0-rc.3:
-------------------------------------------------- --------------------------------------------------

View File

@ -4,9 +4,9 @@ DEFINES += NDEBUG
DEFINES += QT_MESSAGELOGCONTEXT DEFINES += QT_MESSAGELOGCONTEXT
RESOURCES += resources.qrc RESOURCES += resources.qrc
LANGUAGES = zh_CN\ LANGUAGES = zh_CN \
es_AR\ es_AR \
it_IT it_IT
OBJECTS_DIR=obj OBJECTS_DIR=obj
MOC_DIR=moc MOC_DIR=moc
@ -135,6 +135,9 @@ include(thirdparty/qtsingleapplication/src/qtsingleapplication.pri)
INCLUDEPATH += src INCLUDEPATH += src
SOURCES += src/meshstroketifier.cpp
HEADERS += src/meshstroketifier.h
SOURCES += src/autosaver.cpp SOURCES += src/autosaver.cpp
HEADERS += src/autosaver.h HEADERS += src/autosaver.h
@ -144,8 +147,8 @@ HEADERS += src/documentsaver.h
SOURCES += src/normalanddepthmapsgenerator.cpp SOURCES += src/normalanddepthmapsgenerator.cpp
HEADERS += src/normalanddepthmapsgenerator.h HEADERS += src/normalanddepthmapsgenerator.h
SOURCES += src/modelofflinerender.cpp SOURCES += src/modeloffscreenrender.cpp
HEADERS += src/modelofflinerender.h HEADERS += src/modeloffscreenrender.h
SOURCES += src/modelshaderprogram.cpp SOURCES += src/modelshaderprogram.cpp
HEADERS += src/modelshaderprogram.h HEADERS += src/modelshaderprogram.h
@ -198,8 +201,8 @@ HEADERS += src/glbfile.h
SOURCES += src/theme.cpp SOURCES += src/theme.cpp
HEADERS += src/theme.h HEADERS += src/theme.h
SOURCES += src/meshloader.cpp SOURCES += src/model.cpp
HEADERS += src/meshloader.h HEADERS += src/model.h
SOURCES += src/texturegenerator.cpp SOURCES += src/texturegenerator.cpp
HEADERS += src/texturegenerator.h HEADERS += src/texturegenerator.h

View File

@ -399,6 +399,14 @@ Consejos:
<source>Image (*.png)</source> <source>Image (*.png)</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Import...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reverse</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ExportPreviewWidget</name> <name>ExportPreviewWidget</name>
@ -1288,6 +1296,10 @@ Consejos:
<source>Unselect All</source> <source>Unselect All</source>
<translation>Deseleccionar Todo</translation> <translation>Deseleccionar Todo</translation>
</message> </message>
<message>
<source>Reverse</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>UpdatesCheckWidget</name> <name>UpdatesCheckWidget</name>

View File

@ -406,6 +406,14 @@ Suggerimenti:
<source>Image (*.png)</source> <source>Image (*.png)</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Import...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reverse</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ExportPreviewWidget</name> <name>ExportPreviewWidget</name>
@ -1295,6 +1303,10 @@ Suggerimenti:
<source>Unselect All</source> <source>Unselect All</source>
<translation type="unfinished">De-Seleziona tutto</translation> <translation type="unfinished">De-Seleziona tutto</translation>
</message> </message>
<message>
<source>Reverse</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>UpdatesCheckWidget</name> <name>UpdatesCheckWidget</name>

View File

@ -399,6 +399,14 @@ Tips:
<source>Image (*.png)</source> <source>Image (*.png)</source>
<translation> (*.png)</translation> <translation> (*.png)</translation>
</message> </message>
<message>
<source>Import...</source>
<translation>...</translation>
</message>
<message>
<source>Reverse</source>
<translation></translation>
</message>
</context> </context>
<context> <context>
<name>ExportPreviewWidget</name> <name>ExportPreviewWidget</name>
@ -1288,6 +1296,10 @@ Tips:
<source>Colorize</source> <source>Colorize</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>Reverse</source>
<translation></translation>
</message>
</context> </context>
<context> <context>
<name>UpdatesCheckWidget</name> <name>UpdatesCheckWidget</name>

View File

@ -8,6 +8,7 @@
<file>resources/tree-vline.png</file> <file>resources/tree-vline.png</file>
<file>resources/material-demo-model.ds3</file> <file>resources/material-demo-model.ds3</file>
<file>resources/model-addax.ds3</file> <file>resources/model-addax.ds3</file>
<file>resources/model-backpacker.ds3</file>
<file>resources/model-bicycle.ds3</file> <file>resources/model-bicycle.ds3</file>
<file>resources/model-dog.ds3</file> <file>resources/model-dog.ds3</file>
<file>resources/model-cat.ds3</file> <file>resources/model-cat.ds3</file>

View File

@ -1,231 +1,231 @@
DUST3D 1.0 xml 0000000193 DUST3D 1.0 xml 0000000193
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<ds3> <ds3>
<model name="model.xml" offset="0" size="38946"/> <model name="model.xml" offset="0" size="38956"/>
<asset name="canvas.png" offset="38946" size="292117"/> <asset name="canvas.png" offset="38956" size="292117"/>
</ds3> </ds3>
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<canvas originX="0.330693" originY="0.520792" originZ="1.5297" rigType="None"> <canvas originX="0.330693" originY="0.520792" originZ="1.5297" rigType="None">
<nodes> <nodes>
<node id="{021f7f20-d1c4-4b79-94ef-25ecf621ee0c}" partId="{0df76de9-fa79-477d-9771-b85378446b3c}" radius="0.0950495" x="0.330693" y="0.308911" z="0.958416"/> <node id="{021f7f20-d1c4-4b79-94ef-25ecf621ee0c}" partId="{449d0e15-066f-4780-918a-55978d46ed9d}" radius="0.0950495" x="0.330693" y="0.308911" z="0.958416"/>
<node id="{03ff6913-45f1-4508-a39f-3842694cb9f7}" partId="{c722bb89-16f8-4866-b115-74c321caf271}" radius="0.0594059" x="0.425658" y="0.457426" z="1.25445"/> <node id="{03ff6913-45f1-4508-a39f-3842694cb9f7}" partId="{37860592-2626-4c6d-8069-349954bda284}" radius="0.0594059" x="0.425658" y="0.457426" z="1.24508"/>
<node id="{05a4fc6a-8e6f-4c0d-a6ca-023ef4bd8d14}" partId="{e84c3063-265f-4850-9e19-465b84e41118}" radius="0.00990099" x="0.388119" y="0.260972" z="0.906007"/> <node id="{05a4fc6a-8e6f-4c0d-a6ca-023ef4bd8d14}" partId="{8689ab0b-3a56-4f01-b72a-0eee032928d4}" radius="0.00990099" x="0.388119" y="0.260972" z="0.906007"/>
<node id="{06b524b3-b2e5-4e12-993f-497c57aee71e}" partId="{0df76de9-fa79-477d-9771-b85378446b3c}" radius="0.118812" x="0.330693" y="0.368317" z="1.05347"/> <node id="{06b524b3-b2e5-4e12-993f-497c57aee71e}" partId="{449d0e15-066f-4780-918a-55978d46ed9d}" radius="0.118812" x="0.330693" y="0.368317" z="1.05347"/>
<node id="{0da7cc8e-dd93-4b86-a305-fe14ae189790}" partId="{c722bb89-16f8-4866-b115-74c321caf271}" radius="0.0277228" x="0.409901" y="0.653465" z="1.20792"/> <node id="{0da7cc8e-dd93-4b86-a305-fe14ae189790}" partId="{37860592-2626-4c6d-8069-349954bda284}" radius="0.0277228" x="0.409901" y="0.653465" z="1.19855"/>
<node id="{180bd087-9aa6-487a-8b50-5f2231539863}" partId="{92839eb7-0da2-4905-ae58-a1bb3903f444}" radius="0.0316832" x="0.269025" y="0.628687" z="1.83698"/> <node id="{180bd087-9aa6-487a-8b50-5f2231539863}" partId="{0a233c0a-8ce5-42d3-b720-fb97b63d0e58}" radius="0.0316832" x="0.269025" y="0.628687" z="1.83698"/>
<node id="{1a732d47-0948-439d-a173-d0129af7644d}" partId="{a8703d0e-1581-495a-99e0-4507178bf1fa}" radius="0.0158416" x="0.330693" y="0.320792" z="1.75347"/> <node id="{1a732d47-0948-439d-a173-d0129af7644d}" partId="{3719b667-4110-499d-b78b-4f2ec4e66213}" radius="0.0158416" x="0.330693" y="0.320792" z="1.75347"/>
<node id="{1ccfc908-f650-44bc-b072-6d100315b927}" partId="{92839eb7-0da2-4905-ae58-a1bb3903f444}" radius="0.0831683" x="0.288827" y="0.441353" z="1.73794"/> <node id="{1ccfc908-f650-44bc-b072-6d100315b927}" partId="{0a233c0a-8ce5-42d3-b720-fb97b63d0e58}" radius="0.0831683" x="0.288827" y="0.441353" z="1.73794"/>
<node id="{1e3e9eb9-ab4c-4b64-97ad-4342d24b02f9}" partId="{c5bd1431-d740-4266-825d-217513f1a1f5}" radius="0.0118812" x="0.302688" y="0.875247" z="1.33465"/> <node id="{1e3e9eb9-ab4c-4b64-97ad-4342d24b02f9}" partId="{20fce58c-660f-4e93-b97a-a316d0753042}" radius="0.0118812" x="0.302688" y="0.875247" z="1.33465"/>
<node id="{22f090e3-7748-4a48-841a-925de92cce58}" partId="{47d784c2-d5dc-402d-9848-e80e1c03416e}" radius="0.00990099" x="0.437119" y="0.163294" z="1.08515"/> <node id="{22f090e3-7748-4a48-841a-925de92cce58}" partId="{76e8af23-9880-458a-8218-ebf61c0ba341}" radius="0.00990099" x="0.437119" y="0.163294" z="1.08515"/>
<node id="{2498311b-1a9b-4768-bd9c-e108e4f7a1c8}" partId="{a8703d0e-1581-495a-99e0-4507178bf1fa}" radius="0.00792079" x="0.330693" y="0.625743" z="1.93861"/> <node id="{2498311b-1a9b-4768-bd9c-e108e4f7a1c8}" partId="{3719b667-4110-499d-b78b-4f2ec4e66213}" radius="0.00792079" x="0.330693" y="0.625743" z="1.93861"/>
<node id="{259237a6-bac4-4a79-aa57-a74d357feff2}" partId="{c722bb89-16f8-4866-b115-74c321caf271}" radius="0.0217822" x="0.384159" y="0.811881" z="1.34257"/> <node id="{259237a6-bac4-4a79-aa57-a74d357feff2}" partId="{37860592-2626-4c6d-8069-349954bda284}" radius="0.0217822" x="0.384159" y="0.811881" z="1.3332"/>
<node id="{25b71535-a196-49b7-a019-ae08cc75dd60}" partId="{c722bb89-16f8-4866-b115-74c321caf271}" radius="0.0316832" x="0.419802" y="0.629703" z="1.23762"/> <node id="{25b71535-a196-49b7-a019-ae08cc75dd60}" partId="{37860592-2626-4c6d-8069-349954bda284}" radius="0.0316832" x="0.419802" y="0.629703" z="1.22825"/>
<node id="{2bcb3563-c4d3-40bb-8e49-3afe61da902f}" partId="{a8703d0e-1581-495a-99e0-4507178bf1fa}" radius="0.0158416" x="0.330693" y="0.330693" z="1.80198"/> <node id="{2bcb3563-c4d3-40bb-8e49-3afe61da902f}" partId="{3719b667-4110-499d-b78b-4f2ec4e66213}" radius="0.0158416" x="0.330693" y="0.330693" z="1.80198"/>
<node id="{2dd69178-ccf9-4026-9319-9aebbd4155df}" partId="{0df76de9-fa79-477d-9771-b85378446b3c}" radius="0.0356436" x="0.330693" y="0.330693" z="0.786139"/> <node id="{2dd69178-ccf9-4026-9319-9aebbd4155df}" partId="{449d0e15-066f-4780-918a-55978d46ed9d}" radius="0.0356436" x="0.330693" y="0.330693" z="0.786139"/>
<node id="{2e02275f-8aa2-46f3-ad34-579a63890b17}" partId="{e6fced12-025d-471f-b260-6d7854280682}" radius="0.019802" x="0.358982" y="0.942157" z="1.62797"/> <node id="{2e02275f-8aa2-46f3-ad34-579a63890b17}" partId="{57a41558-46e2-48ce-9f6b-54ada6b2d083}" radius="0.019802" x="0.358982" y="0.942157" z="1.62797"/>
<node id="{2ea7a440-8ff5-4143-ab72-ebe178087607}" partId="{92839eb7-0da2-4905-ae58-a1bb3903f444}" radius="0.029703" x="0.302688" y="0.986613" z="1.94599"/> <node id="{2ea7a440-8ff5-4143-ab72-ebe178087607}" partId="{0a233c0a-8ce5-42d3-b720-fb97b63d0e58}" radius="0.029703" x="0.302688" y="0.986613" z="1.94599"/>
<node id="{31ccee3b-ba23-4294-ae81-6f6b34f1ed5d}" partId="{6df97180-d57c-46b3-8f96-4173b69abbc5}" radius="0.019802" x="0.409901" y="0.170784" z="0.957782"/> <node id="{31ccee3b-ba23-4294-ae81-6f6b34f1ed5d}" partId="{0b0f5c6e-94d4-4c8e-9997-052a70fa3720}" radius="0.019802" x="0.409901" y="0.170784" z="0.957782"/>
<node id="{350d2902-c60e-4cba-a10c-d000ecba47ad}" partId="{c5bd1431-d740-4266-825d-217513f1a1f5}" radius="0.0118812" x="0.304668" y="0.950495" z="1.32277"/> <node id="{350d2902-c60e-4cba-a10c-d000ecba47ad}" partId="{20fce58c-660f-4e93-b97a-a316d0753042}" radius="0.0118812" x="0.304668" y="0.950495" z="1.32277"/>
<node id="{355833f3-bd2d-4508-9293-dbf33c65222b}" partId="{a8703d0e-1581-495a-99e0-4507178bf1fa}" radius="0.0158416" x="0.330693" y="0.59604" z="1.92079"/> <node id="{355833f3-bd2d-4508-9293-dbf33c65222b}" partId="{3719b667-4110-499d-b78b-4f2ec4e66213}" radius="0.0158416" x="0.330693" y="0.59604" z="1.92079"/>
<node id="{37dc112b-cfb9-4a87-9b82-90a27c9d1518}" partId="{0df76de9-fa79-477d-9771-b85378446b3c}" radius="0.122772" x="0.330693" y="0.39604" z="1.10693"/> <node id="{37dc112b-cfb9-4a87-9b82-90a27c9d1518}" partId="{449d0e15-066f-4780-918a-55978d46ed9d}" radius="0.122772" x="0.330693" y="0.39604" z="1.10693"/>
<node id="{3e13f116-1dd9-4024-8b1b-764c176a7835}" partId="{a8703d0e-1581-495a-99e0-4507178bf1fa}" radius="0.00792079" x="0.330693" y="0.439604" z="1.90297"/> <node id="{3e13f116-1dd9-4024-8b1b-764c176a7835}" partId="{3719b667-4110-499d-b78b-4f2ec4e66213}" radius="0.00792079" x="0.330693" y="0.439604" z="1.90297"/>
<node id="{40529c5a-bafd-4bc1-84c3-daae168d5da8}" partId="{e6fced12-025d-471f-b260-6d7854280682}" radius="0.0158416" x="0.368883" y="0.779908" z="1.69841"/> <node id="{40529c5a-bafd-4bc1-84c3-daae168d5da8}" partId="{57a41558-46e2-48ce-9f6b-54ada6b2d083}" radius="0.0158416" x="0.368883" y="0.779908" z="1.69841"/>
<node id="{4123d281-63d6-44a5-bdfd-62261ab8dad9}" partId="{c722bb89-16f8-4866-b115-74c321caf271}" radius="0.0158416" x="0.384159" y="0.780198" z="1.26931"/> <node id="{4123d281-63d6-44a5-bdfd-62261ab8dad9}" partId="{37860592-2626-4c6d-8069-349954bda284}" radius="0.0158416" x="0.384159" y="0.780198" z="1.25994"/>
<node id="{4ab72941-0a5e-442f-919f-aff60341427b}" partId="{c5bd1431-d740-4266-825d-217513f1a1f5}" radius="0.019802" x="0.306648" y="0.930692" z="1.34059"/> <node id="{4ab72941-0a5e-442f-919f-aff60341427b}" partId="{20fce58c-660f-4e93-b97a-a316d0753042}" radius="0.019802" x="0.306648" y="0.930692" z="1.34059"/>
<node id="{4c9ba403-391c-4903-a8ba-0ff250713286}" partId="{c5bd1431-d740-4266-825d-217513f1a1f5}" radius="0.039604" x="0.255163" y="0.611881" z="1.29505"/> <node id="{4c9ba403-391c-4903-a8ba-0ff250713286}" partId="{20fce58c-660f-4e93-b97a-a316d0753042}" radius="0.039604" x="0.255163" y="0.611881" z="1.29505"/>
<node id="{4d605b53-37e2-4622-a9df-b457e4b1ae89}" partId="{e6fced12-025d-471f-b260-6d7854280682}" radius="0.0831683" x="0.376803" y="0.428676" z="1.7314"/> <node id="{4d605b53-37e2-4622-a9df-b457e4b1ae89}" partId="{57a41558-46e2-48ce-9f6b-54ada6b2d083}" radius="0.0831683" x="0.376803" y="0.428676" z="1.7314"/>
<node id="{4ff2a8f5-aade-4320-98c5-f78abbf31df0}" partId="{0df76de9-fa79-477d-9771-b85378446b3c}" radius="0.154455" x="0.330693" y="0.441584" z="1.45941"/> <node id="{4ff2a8f5-aade-4320-98c5-f78abbf31df0}" partId="{449d0e15-066f-4780-918a-55978d46ed9d}" radius="0.154455" x="0.330693" y="0.441584" z="1.45941"/>
<node id="{54127252-0ecb-4a08-84b8-489ce894aebe}" partId="{92839eb7-0da2-4905-ae58-a1bb3903f444}" radius="0.0792079" x="0.259123" y="0.500455" z="1.76833"/> <node id="{54127252-0ecb-4a08-84b8-489ce894aebe}" partId="{0a233c0a-8ce5-42d3-b720-fb97b63d0e58}" radius="0.0792079" x="0.259123" y="0.500455" z="1.76833"/>
<node id="{5526b16b-07a8-44ae-a2a1-23edd995a34e}" partId="{c5bd1431-d740-4266-825d-217513f1a1f5}" radius="0.0158416" x="0.304668" y="0.90495" z="1.33862"/> <node id="{5526b16b-07a8-44ae-a2a1-23edd995a34e}" partId="{20fce58c-660f-4e93-b97a-a316d0753042}" radius="0.0158416" x="0.304668" y="0.90495" z="1.33862"/>
<node id="{59c85ebe-b47b-4d4b-b40a-9bb0ed434914}" partId="{c722bb89-16f8-4866-b115-74c321caf271}" radius="0.0594059" x="0.443023" y="0.568858" z="1.2982"/> <node id="{59c85ebe-b47b-4d4b-b40a-9bb0ed434914}" partId="{37860592-2626-4c6d-8069-349954bda284}" radius="0.0594059" x="0.443023" y="0.568858" z="1.28883"/>
<node id="{5b25a3a7-ed5a-42ef-a277-6fd8406649f2}" partId="{6df97180-d57c-46b3-8f96-4173b69abbc5}" radius="0.0237624" x="0.352476" y="0.235317" z="0.906707"/> <node id="{5b25a3a7-ed5a-42ef-a277-6fd8406649f2}" partId="{0b0f5c6e-94d4-4c8e-9997-052a70fa3720}" radius="0.0237624" x="0.352476" y="0.235317" z="0.906707"/>
<node id="{5f73c8dd-e8c1-484c-af35-5b2789c0582c}" partId="{a8703d0e-1581-495a-99e0-4507178bf1fa}" radius="0.005" x="0.330693" y="0.635644" z="1.95644"/> <node id="{5f73c8dd-e8c1-484c-af35-5b2789c0582c}" partId="{3719b667-4110-499d-b78b-4f2ec4e66213}" radius="0.005" x="0.330693" y="0.635644" z="1.95644"/>
<node id="{6183e755-7d06-4e96-aee0-2e4a5df4c83f}" partId="{e84c3063-265f-4850-9e19-465b84e41118}" radius="0.00594059" x="0.40198" y="0.25849" z="0.918282"/> <node id="{6183e755-7d06-4e96-aee0-2e4a5df4c83f}" partId="{8689ab0b-3a56-4f01-b72a-0eee032928d4}" radius="0.00594059" x="0.40198" y="0.25849" z="0.918282"/>
<node id="{6281b6cb-d0ae-43b2-b124-26fcedacf472}" partId="{c5bd1431-d740-4266-825d-217513f1a1f5}" radius="0.0633663" x="0.245262" y="0.520792" z="1.27921"/> <node id="{6281b6cb-d0ae-43b2-b124-26fcedacf472}" partId="{20fce58c-660f-4e93-b97a-a316d0753042}" radius="0.0633663" x="0.245262" y="0.520792" z="1.27921"/>
<node id="{67afd8c7-012e-4db6-8de5-b2cb35cfbc50}" partId="{0df76de9-fa79-477d-9771-b85378446b3c}" radius="0.0752475" x="0.330693" y="0.386139" z="1.76832"/> <node id="{67afd8c7-012e-4db6-8de5-b2cb35cfbc50}" partId="{449d0e15-066f-4780-918a-55978d46ed9d}" radius="0.0752475" x="0.330693" y="0.386139" z="1.76832"/>
<node id="{68fee6f0-597d-4079-a61b-4def65e1c8f9}" partId="{6df97180-d57c-46b3-8f96-4173b69abbc5}" radius="0.019802" x="0.437624" y="0.149406" z="0.992491"/> <node id="{68fee6f0-597d-4079-a61b-4def65e1c8f9}" partId="{0b0f5c6e-94d4-4c8e-9997-052a70fa3720}" radius="0.019802" x="0.437624" y="0.149406" z="0.992491"/>
<node id="{6b0da1e9-337b-4cf4-9d08-c2ab19b18c80}" partId="{47d784c2-d5dc-402d-9848-e80e1c03416e}" radius="0.0257426" x="0.37864" y="0.224511" z="0.978218"/> <node id="{6b0da1e9-337b-4cf4-9d08-c2ab19b18c80}" partId="{76e8af23-9880-458a-8218-ebf61c0ba341}" radius="0.0257426" x="0.37864" y="0.224511" z="0.978218"/>
<node id="{6b2b333d-2abf-4d6e-8957-7a848c46e651}" partId="{a8703d0e-1581-495a-99e0-4507178bf1fa}" radius="0.0118812" x="0.330693" y="0.475248" z="1.91683"/> <node id="{6b2b333d-2abf-4d6e-8957-7a848c46e651}" partId="{3719b667-4110-499d-b78b-4f2ec4e66213}" radius="0.0118812" x="0.330693" y="0.475248" z="1.91683"/>
<node id="{6c08eb60-5120-4ae2-9854-df02e5ebf9c8}" partId="{a8703d0e-1581-495a-99e0-4507178bf1fa}" radius="0.0118812" x="0.330693" y="0.378218" z="1.86337"/> <node id="{6c08eb60-5120-4ae2-9854-df02e5ebf9c8}" partId="{3719b667-4110-499d-b78b-4f2ec4e66213}" radius="0.0118812" x="0.330693" y="0.378218" z="1.86337"/>
<node id="{71ea7967-b59e-4507-bf55-225882ecbba5}" partId="{c5bd1431-d740-4266-825d-217513f1a1f5}" radius="0.0594059" x="0.245263" y="0.576237" z="1.29109"/> <node id="{71ea7967-b59e-4507-bf55-225882ecbba5}" partId="{20fce58c-660f-4e93-b97a-a316d0753042}" radius="0.0594059" x="0.245263" y="0.576237" z="1.29109"/>
<node id="{72a946b4-b2a0-426a-884b-5cf5176f9ece}" partId="{47d784c2-d5dc-402d-9848-e80e1c03416e}" radius="0.0257426" x="0.409418" y="0.199587" z="1.03762"/> <node id="{72a946b4-b2a0-426a-884b-5cf5176f9ece}" partId="{76e8af23-9880-458a-8218-ebf61c0ba341}" radius="0.0257426" x="0.409418" y="0.199587" z="1.03762"/>
<node id="{734b73e6-9175-42d8-8ffe-057b933bc666}" partId="{c722bb89-16f8-4866-b115-74c321caf271}" radius="0.029703" x="0.386139" y="0.805941" z="1.36634"/> <node id="{734b73e6-9175-42d8-8ffe-057b933bc666}" partId="{37860592-2626-4c6d-8069-349954bda284}" radius="0.029703" x="0.386139" y="0.805941" z="1.35697"/>
<node id="{74399604-82fe-4cc0-a61f-0de6f297f80b}" partId="{e6fced12-025d-471f-b260-6d7854280682}" radius="0.0277228" x="0.374823" y="0.725738" z="1.71259"/> <node id="{74399604-82fe-4cc0-a61f-0de6f297f80b}" partId="{57a41558-46e2-48ce-9f6b-54ada6b2d083}" radius="0.0277228" x="0.374823" y="0.725738" z="1.71259"/>
<node id="{7615ca8a-d9d4-4987-84b1-7c5578ac55f0}" partId="{c722bb89-16f8-4866-b115-74c321caf271}" radius="0.0158416" x="0.39208" y="0.716832" z="1.20198"/> <node id="{7615ca8a-d9d4-4987-84b1-7c5578ac55f0}" partId="{37860592-2626-4c6d-8069-349954bda284}" radius="0.0158416" x="0.39208" y="0.716832" z="1.19261"/>
<node id="{797ac170-8b69-423e-8ca5-acb7217188be}" partId="{6df97180-d57c-46b3-8f96-4173b69abbc5}" radius="0.005" x="0.431683" y="0.0075189" z="1.3375"/> <node id="{797ac170-8b69-423e-8ca5-acb7217188be}" partId="{0b0f5c6e-94d4-4c8e-9997-052a70fa3720}" radius="0.005" x="0.431683" y="0.0075189" z="1.3375"/>
<node id="{7af1da13-55ff-4ce7-9fcd-ab9825098cb8}" partId="{0df76de9-fa79-477d-9771-b85378446b3c}" radius="0.0435644" x="0.330693" y="0.314851" z="0.829703"/> <node id="{7af1da13-55ff-4ce7-9fcd-ab9825098cb8}" partId="{449d0e15-066f-4780-918a-55978d46ed9d}" radius="0.0435644" x="0.330693" y="0.314851" z="0.829703"/>
<node id="{7cfbf2a4-3d92-4357-8644-4e195dfac673}" partId="{a8703d0e-1581-495a-99e0-4507178bf1fa}" radius="0.0158416" x="0.330693" y="0.518812" z="1.92079"/> <node id="{7cfbf2a4-3d92-4357-8644-4e195dfac673}" partId="{3719b667-4110-499d-b78b-4f2ec4e66213}" radius="0.0158416" x="0.330693" y="0.518812" z="1.92079"/>
<node id="{7d3f47b9-2de0-46e2-9691-3c7de959777f}" partId="{c5bd1431-d740-4266-825d-217513f1a1f5}" radius="0.0158416" x="0.296747" y="0.817822" z="1.32871"/> <node id="{7d3f47b9-2de0-46e2-9691-3c7de959777f}" partId="{20fce58c-660f-4e93-b97a-a316d0753042}" radius="0.0158416" x="0.296747" y="0.817822" z="1.32871"/>
<node id="{7f6a90b8-57d3-4319-8bd3-c7c1733f52f5}" partId="{0df76de9-fa79-477d-9771-b85378446b3c}" radius="0.130693" x="0.330693" y="0.415842" z="1.70891"/> <node id="{7f6a90b8-57d3-4319-8bd3-c7c1733f52f5}" partId="{449d0e15-066f-4780-918a-55978d46ed9d}" radius="0.130693" x="0.330693" y="0.415842" z="1.70891"/>
<node id="{7fd2060f-8360-4fb2-a9c9-ba773bcb4fc6}" partId="{e6fced12-025d-471f-b260-6d7854280682}" radius="0.0217822" x="0.360962" y="0.963494" z="1.58918"/> <node id="{7fd2060f-8360-4fb2-a9c9-ba773bcb4fc6}" partId="{57a41558-46e2-48ce-9f6b-54ada6b2d083}" radius="0.0217822" x="0.360962" y="0.963494" z="1.58918"/>
<node id="{84c39fc7-a579-453f-b2d2-3872af219174}" partId="{6df97180-d57c-46b3-8f96-4173b69abbc5}" radius="0.019802" x="0.407921" y="0.126117" z="1.05069"/> <node id="{84c39fc7-a579-453f-b2d2-3872af219174}" partId="{0b0f5c6e-94d4-4c8e-9997-052a70fa3720}" radius="0.019802" x="0.407921" y="0.126117" z="1.05069"/>
<node id="{883859a6-0102-4a26-8596-3c47bcaf1970}" partId="{a8703d0e-1581-495a-99e0-4507178bf1fa}" radius="0.019802" x="0.330693" y="0.558416" z="1.91683"/> <node id="{883859a6-0102-4a26-8596-3c47bcaf1970}" partId="{3719b667-4110-499d-b78b-4f2ec4e66213}" radius="0.019802" x="0.330693" y="0.558416" z="1.91683"/>
<node id="{89836264-59bd-420a-84d1-dae0a7d98f27}" partId="{0df76de9-fa79-477d-9771-b85378446b3c}" radius="0.154455" x="0.330693" y="0.419802" z="1.24752"/> <node id="{89836264-59bd-420a-84d1-dae0a7d98f27}" partId="{449d0e15-066f-4780-918a-55978d46ed9d}" radius="0.154455" x="0.330693" y="0.419802" z="1.24752"/>
<node id="{8a9ec61d-2d9c-46f1-a0da-d04b5ee65085}" partId="{92839eb7-0da2-4905-ae58-a1bb3903f444}" radius="0.019802" x="0.306648" y="0.913681" z="1.96436"/> <node id="{8a9ec61d-2d9c-46f1-a0da-d04b5ee65085}" partId="{0a233c0a-8ce5-42d3-b720-fb97b63d0e58}" radius="0.019802" x="0.306648" y="0.913681" z="1.96436"/>
<node id="{8ae179bd-6309-4cfa-b892-5a040d1b75df}" partId="{c722bb89-16f8-4866-b115-74c321caf271}" radius="0.0118812" x="0.384159" y="0.807921" z="1.31881"/> <node id="{8ae179bd-6309-4cfa-b892-5a040d1b75df}" partId="{37860592-2626-4c6d-8069-349954bda284}" radius="0.0118812" x="0.384159" y="0.807921" z="1.30944"/>
<node id="{8da76f01-7c78-4227-a64e-af317cb591d5}" partId="{92839eb7-0da2-4905-ae58-a1bb3903f444}" radius="0.0158416" x="0.296747" y="0.741714" z="1.92297"/> <node id="{8da76f01-7c78-4227-a64e-af317cb591d5}" partId="{0a233c0a-8ce5-42d3-b720-fb97b63d0e58}" radius="0.0158416" x="0.296747" y="0.741714" z="1.92297"/>
<node id="{913b6427-0ac2-4ace-ae9d-10434c1b5bdf}" partId="{0df76de9-fa79-477d-9771-b85378446b3c}" radius="0.0277228" x="0.330693" y="0.350495" z="0.763367"/> <node id="{913b6427-0ac2-4ace-ae9d-10434c1b5bdf}" partId="{449d0e15-066f-4780-918a-55978d46ed9d}" radius="0.0277228" x="0.330693" y="0.350495" z="0.763367"/>
<node id="{936ea324-7ae0-4fe3-be1c-bc3f43bbcd7b}" partId="{92839eb7-0da2-4905-ae58-a1bb3903f444}" radius="0.0594059" x="0.257144" y="0.55998" z="1.78688"/> <node id="{936ea324-7ae0-4fe3-be1c-bc3f43bbcd7b}" partId="{0a233c0a-8ce5-42d3-b720-fb97b63d0e58}" radius="0.0594059" x="0.257144" y="0.55998" z="1.78688"/>
<node id="{9ba73d77-9c23-40eb-97c3-b6d6c8ebd6d9}" partId="{0df76de9-fa79-477d-9771-b85378446b3c}" radius="0.150495" x="0.330693" y="0.439604" z="1.5802"/> <node id="{9ba73d77-9c23-40eb-97c3-b6d6c8ebd6d9}" partId="{449d0e15-066f-4780-918a-55978d46ed9d}" radius="0.150495" x="0.330693" y="0.439604" z="1.5802"/>
<node id="{a2b78111-e6f4-4389-b7ed-4ba0a55599e8}" partId="{92839eb7-0da2-4905-ae58-a1bb3903f444}" radius="0.0118812" x="0.304668" y="0.933528" z="1.95106"/> <node id="{a2b78111-e6f4-4389-b7ed-4ba0a55599e8}" partId="{0a233c0a-8ce5-42d3-b720-fb97b63d0e58}" radius="0.0118812" x="0.304668" y="0.933528" z="1.95106"/>
<node id="{a30b2cb5-7893-4db3-8fe8-269d30bca548}" partId="{c5bd1431-d740-4266-825d-217513f1a1f5}" radius="0.029703" x="0.302688" y="0.988119" z="1.29109"/> <node id="{a30b2cb5-7893-4db3-8fe8-269d30bca548}" partId="{20fce58c-660f-4e93-b97a-a316d0753042}" radius="0.029703" x="0.302688" y="0.988119" z="1.29109"/>
<node id="{a459f13d-414a-454f-8739-97ddba88d0ad}" partId="{6df97180-d57c-46b3-8f96-4173b69abbc5}" radius="0.0118812" x="0.419802" y="0.038863" z="1.24557"/> <node id="{a459f13d-414a-454f-8739-97ddba88d0ad}" partId="{0b0f5c6e-94d4-4c8e-9997-052a70fa3720}" radius="0.0118812" x="0.419802" y="0.038863" z="1.24557"/>
<node id="{ab7d093a-d8de-4328-97fa-d9ed37aec364}" partId="{c722bb89-16f8-4866-b115-74c321caf271}" radius="0.019802" x="0.382179" y="0.794059" z="1.29901"/> <node id="{ab7d093a-d8de-4328-97fa-d9ed37aec364}" partId="{37860592-2626-4c6d-8069-349954bda284}" radius="0.019802" x="0.382179" y="0.794059" z="1.28964"/>
<node id="{b0e81284-8fbc-4757-a52e-c485fe57d92c}" partId="{92839eb7-0da2-4905-ae58-a1bb3903f444}" radius="0.0277228" x="0.290807" y="0.689913" z="1.9017"/> <node id="{b0e81284-8fbc-4757-a52e-c485fe57d92c}" partId="{0a233c0a-8ce5-42d3-b720-fb97b63d0e58}" radius="0.0277228" x="0.290807" y="0.689913" z="1.9017"/>
<node id="{b10279cc-27c0-470f-8614-72a45ad74fb2}" partId="{c722bb89-16f8-4866-b115-74c321caf271}" radius="0.0118812" x="0.386139" y="0.750495" z="1.23564"/> <node id="{b10279cc-27c0-470f-8614-72a45ad74fb2}" partId="{37860592-2626-4c6d-8069-349954bda284}" radius="0.0118812" x="0.386139" y="0.750495" z="1.22627"/>
<node id="{b6d23fe7-3bd6-4a4f-9601-0661304b1c94}" partId="{47d784c2-d5dc-402d-9848-e80e1c03416e}" radius="0.0178218" x="0.36479" y="0.235726" z="0.953465"/> <node id="{b6d23fe7-3bd6-4a4f-9601-0661304b1c94}" partId="{76e8af23-9880-458a-8218-ebf61c0ba341}" radius="0.0178218" x="0.36479" y="0.235726" z="0.953465"/>
<node id="{b7527147-9500-4627-8704-5e363c3a2779}" partId="{92839eb7-0da2-4905-ae58-a1bb3903f444}" radius="0.039604" x="0.255163" y="0.586955" z="1.8011"/> <node id="{b7527147-9500-4627-8704-5e363c3a2779}" partId="{0a233c0a-8ce5-42d3-b720-fb97b63d0e58}" radius="0.039604" x="0.255163" y="0.586955" z="1.8011"/>
<node id="{b7c80ca9-246f-4b6f-84cb-c3642691cf76}" partId="{c722bb89-16f8-4866-b115-74c321caf271}" radius="0.0633663" x="0.446984" y="0.515123" z="1.28317"/> <node id="{b7c80ca9-246f-4b6f-84cb-c3642691cf76}" partId="{37860592-2626-4c6d-8069-349954bda284}" radius="0.0633663" x="0.446984" y="0.515123" z="1.2738"/>
<node id="{b88113fa-d829-40e0-95ef-0462032ce7b9}" partId="{e6fced12-025d-471f-b260-6d7854280682}" radius="0.0316832" x="0.396605" y="0.637891" z="1.69775"/> <node id="{b88113fa-d829-40e0-95ef-0462032ce7b9}" partId="{57a41558-46e2-48ce-9f6b-54ada6b2d083}" radius="0.0316832" x="0.396605" y="0.637891" z="1.69775"/>
<node id="{baae591e-9437-454a-ba87-ea28bbde87c9}" partId="{e6fced12-025d-471f-b260-6d7854280682}" radius="0.029703" x="0.362942" y="0.989347" z="1.56941"/> <node id="{baae591e-9437-454a-ba87-ea28bbde87c9}" partId="{57a41558-46e2-48ce-9f6b-54ada6b2d083}" radius="0.029703" x="0.362942" y="0.989347" z="1.56941"/>
<node id="{bba5b320-733b-44a4-a701-27f12003dde9}" partId="{c722bb89-16f8-4866-b115-74c321caf271}" radius="0.0316832" x="0.39802" y="0.683168" z="1.1802"/> <node id="{bba5b320-733b-44a4-a701-27f12003dde9}" partId="{37860592-2626-4c6d-8069-349954bda284}" radius="0.0316832" x="0.39802" y="0.683168" z="1.17083"/>
<node id="{bcb2d6a7-7a55-424a-84a9-ef7ba7e42731}" partId="{6df97180-d57c-46b3-8f96-4173b69abbc5}" radius="0.0158416" x="0.392079" y="0.112624" z="1.12511"/> <node id="{bcb2d6a7-7a55-424a-84a9-ef7ba7e42731}" partId="{0b0f5c6e-94d4-4c8e-9997-052a70fa3720}" radius="0.0158416" x="0.392079" y="0.112624" z="1.12511"/>
<node id="{bff67e53-359b-4e50-bafb-dcc9dd84f4ec}" partId="{92839eb7-0da2-4905-ae58-a1bb3903f444}" radius="0.0277228" x="0.278926" y="0.655904" z="1.85913"/> <node id="{bff67e53-359b-4e50-bafb-dcc9dd84f4ec}" partId="{0a233c0a-8ce5-42d3-b720-fb97b63d0e58}" radius="0.0277228" x="0.278926" y="0.655904" z="1.85913"/>
<node id="{c0000c88-edd1-46ca-b57b-9dc6874f72eb}" partId="{6df97180-d57c-46b3-8f96-4173b69abbc5}" radius="0.0158416" x="0.405941" y="0.0482526" z="1.19816"/> <node id="{c0000c88-edd1-46ca-b57b-9dc6874f72eb}" partId="{0b0f5c6e-94d4-4c8e-9997-052a70fa3720}" radius="0.0158416" x="0.405941" y="0.0482526" z="1.19816"/>
<node id="{c03f9808-ad09-4e34-8683-225be168d659}" partId="{c5bd1431-d740-4266-825d-217513f1a1f5}" radius="0.0237624" x="0.290807" y="0.756436" z="1.31881"/> <node id="{c03f9808-ad09-4e34-8683-225be168d659}" partId="{20fce58c-660f-4e93-b97a-a316d0753042}" radius="0.0237624" x="0.290807" y="0.756436" z="1.31881"/>
<node id="{c41b7308-14bf-4950-b2ba-2775199a92e0}" partId="{c5bd1431-d740-4266-825d-217513f1a1f5}" radius="0.0316832" x="0.269025" y="0.663366" z="1.30693"/> <node id="{c41b7308-14bf-4950-b2ba-2775199a92e0}" partId="{20fce58c-660f-4e93-b97a-a316d0753042}" radius="0.0316832" x="0.269025" y="0.663366" z="1.30693"/>
<node id="{c6050850-281f-41f3-b736-c734179996c3}" partId="{6df97180-d57c-46b3-8f96-4173b69abbc5}" radius="0.0118812" x="0.431683" y="0.0294736" z="1.27119"/> <node id="{c6050850-281f-41f3-b736-c734179996c3}" partId="{0b0f5c6e-94d4-4c8e-9997-052a70fa3720}" radius="0.0118812" x="0.431683" y="0.0294736" z="1.27119"/>
<node id="{c6ab722d-0df4-46df-a666-c08534fa785e}" partId="{e6fced12-025d-471f-b260-6d7854280682}" radius="0.0158416" x="0.360962" y="0.886031" z="1.65148"/> <node id="{c6ab722d-0df4-46df-a666-c08534fa785e}" partId="{57a41558-46e2-48ce-9f6b-54ada6b2d083}" radius="0.0158416" x="0.360962" y="0.886031" z="1.65148"/>
<node id="{ca57c536-2786-4b36-a3d9-9590b9658843}" partId="{c5bd1431-d740-4266-825d-217513f1a1f5}" radius="0.0277228" x="0.278926" y="0.69901" z="1.30891"/> <node id="{ca57c536-2786-4b36-a3d9-9590b9658843}" partId="{20fce58c-660f-4e93-b97a-a316d0753042}" radius="0.0277228" x="0.278926" y="0.69901" z="1.30891"/>
<node id="{cda8ff54-1879-450f-8af9-a118ff6bfd7f}" partId="{0df76de9-fa79-477d-9771-b85378446b3c}" radius="0.162376" x="0.330693" y="0.411881" z="1.33663"/> <node id="{cda8ff54-1879-450f-8af9-a118ff6bfd7f}" partId="{449d0e15-066f-4780-918a-55978d46ed9d}" radius="0.162376" x="0.330693" y="0.411881" z="1.33663"/>
<node id="{d32e752f-fdd0-4ccc-8558-f341ceef5f91}" partId="{6df97180-d57c-46b3-8f96-4173b69abbc5}" radius="0.00792079" x="0.433663" y="0.0193337" z="1.30536"/> <node id="{d32e752f-fdd0-4ccc-8558-f341ceef5f91}" partId="{0b0f5c6e-94d4-4c8e-9997-052a70fa3720}" radius="0.00792079" x="0.433663" y="0.0193337" z="1.30536"/>
<node id="{d46924c8-54d7-4f2b-8376-b5e12a7b535c}" partId="{92839eb7-0da2-4905-ae58-a1bb3903f444}" radius="0.0118812" x="0.302688" y="0.799967" z="1.93784"/> <node id="{d46924c8-54d7-4f2b-8376-b5e12a7b535c}" partId="{0a233c0a-8ce5-42d3-b720-fb97b63d0e58}" radius="0.0118812" x="0.302688" y="0.799967" z="1.93784"/>
<node id="{d6fd2f1a-7c90-4c62-82be-1eeb781c5847}" partId="{6df97180-d57c-46b3-8f96-4173b69abbc5}" radius="0.0158416" x="0.39604" y="0.0777943" z="1.16925"/> <node id="{d6fd2f1a-7c90-4c62-82be-1eeb781c5847}" partId="{0b0f5c6e-94d4-4c8e-9997-052a70fa3720}" radius="0.0158416" x="0.39604" y="0.0777943" z="1.16925"/>
<node id="{d71693ae-f238-4bd3-881e-bcdee119c22c}" partId="{c5bd1431-d740-4266-825d-217513f1a1f5}" radius="0.0217822" x="0.304668" y="0.974257" z="1.29901"/> <node id="{d71693ae-f238-4bd3-881e-bcdee119c22c}" partId="{20fce58c-660f-4e93-b97a-a316d0753042}" radius="0.0217822" x="0.304668" y="0.974257" z="1.29901"/>
<node id="{d884eda9-29ca-41ab-8a45-3995c8058549}" partId="{e6fced12-025d-471f-b260-6d7854280682}" radius="0.039604" x="0.410467" y="0.582969" z="1.69421"/> <node id="{d884eda9-29ca-41ab-8a45-3995c8058549}" partId="{57a41558-46e2-48ce-9f6b-54ada6b2d083}" radius="0.039604" x="0.410467" y="0.582969" z="1.69421"/>
<node id="{dd3e9c97-c506-4faf-9a47-d0003c62cf33}" partId="{47d784c2-d5dc-402d-9848-e80e1c03416e}" radius="0.0178218" x="0.423268" y="0.184411" z="1.06535"/> <node id="{dd3e9c97-c506-4faf-9a47-d0003c62cf33}" partId="{76e8af23-9880-458a-8218-ebf61c0ba341}" radius="0.0178218" x="0.423268" y="0.184411" z="1.06535"/>
<node id="{dd48d6f8-0172-4886-a11a-6c9f36444be5}" partId="{c5bd1431-d740-4266-825d-217513f1a1f5}" radius="0.0594059" x="0.251203" y="0.457426" z="1.26435"/> <node id="{dd48d6f8-0172-4886-a11a-6c9f36444be5}" partId="{20fce58c-660f-4e93-b97a-a316d0753042}" radius="0.0594059" x="0.251203" y="0.457426" z="1.26435"/>
<node id="{ded04835-2d99-46a0-8763-7869526dd8be}" partId="{92839eb7-0da2-4905-ae58-a1bb3903f444}" radius="0.0158416" x="0.304668" y="0.854708" z="1.94936"/> <node id="{ded04835-2d99-46a0-8763-7869526dd8be}" partId="{0a233c0a-8ce5-42d3-b720-fb97b63d0e58}" radius="0.0158416" x="0.304668" y="0.854708" z="1.94936"/>
<node id="{e274e844-fbf1-47fb-bbec-0504785f3c11}" partId="{a8703d0e-1581-495a-99e0-4507178bf1fa}" radius="0.0158416" x="0.330693" y="0.350495" z="1.8396"/> <node id="{e274e844-fbf1-47fb-bbec-0504785f3c11}" partId="{3719b667-4110-499d-b78b-4f2ec4e66213}" radius="0.0158416" x="0.330693" y="0.350495" z="1.8396"/>
<node id="{e3deddc7-edcd-4bdf-b0bd-f2d46e8bef63}" partId="{47d784c2-d5dc-402d-9848-e80e1c03416e}" radius="0.0217822" x="0.398646" y="0.20831" z="1.00792"/> <node id="{e3deddc7-edcd-4bdf-b0bd-f2d46e8bef63}" partId="{76e8af23-9880-458a-8218-ebf61c0ba341}" radius="0.0217822" x="0.398646" y="0.20831" z="1.00792"/>
<node id="{e40576eb-3d06-4c53-8f29-98683f2d7aab}" partId="{e6fced12-025d-471f-b260-6d7854280682}" radius="0.0594059" x="0.408486" y="0.552868" z="1.69909"/> <node id="{e40576eb-3d06-4c53-8f29-98683f2d7aab}" partId="{57a41558-46e2-48ce-9f6b-54ada6b2d083}" radius="0.0594059" x="0.408486" y="0.552868" z="1.69909"/>
<node id="{e5361db3-58e4-41af-a955-55010430d3a7}" partId="{e6fced12-025d-471f-b260-6d7854280682}" radius="0.0277228" x="0.386704" y="0.672958" z="1.69906"/> <node id="{e5361db3-58e4-41af-a955-55010430d3a7}" partId="{57a41558-46e2-48ce-9f6b-54ada6b2d083}" radius="0.0277228" x="0.386704" y="0.672958" z="1.69906"/>
<node id="{e5d65034-602c-4336-a919-b4a8ff8fc68f}" partId="{0df76de9-fa79-477d-9771-b85378446b3c}" radius="0.0752475" x="0.330693" y="0.29505" z="0.893069"/> <node id="{e5d65034-602c-4336-a919-b4a8ff8fc68f}" partId="{449d0e15-066f-4780-918a-55978d46ed9d}" radius="0.0752475" x="0.330693" y="0.29505" z="0.893069"/>
<node id="{e97e7112-dae7-4465-8cb2-d1d66fc81dee}" partId="{c722bb89-16f8-4866-b115-74c321caf271}" radius="0.039604" x="0.433664" y="0.59802" z="1.26931"/> <node id="{e97e7112-dae7-4465-8cb2-d1d66fc81dee}" partId="{37860592-2626-4c6d-8069-349954bda284}" radius="0.039604" x="0.433664" y="0.59802" z="1.25994"/>
<node id="{e9fd4236-5ce0-4d2e-8868-d894967fb749}" partId="{e6fced12-025d-471f-b260-6d7854280682}" radius="0.0118812" x="0.360962" y="0.950003" z="1.6054"/> <node id="{e9fd4236-5ce0-4d2e-8868-d894967fb749}" partId="{57a41558-46e2-48ce-9f6b-54ada6b2d083}" radius="0.0118812" x="0.360962" y="0.950003" z="1.6054"/>
<node id="{eb75e8d6-9b28-4ec5-afa6-59b006152425}" partId="{92839eb7-0da2-4905-ae58-a1bb3903f444}" radius="0.0217822" x="0.304668" y="0.954068" z="1.94622"/> <node id="{eb75e8d6-9b28-4ec5-afa6-59b006152425}" partId="{0a233c0a-8ce5-42d3-b720-fb97b63d0e58}" radius="0.0217822" x="0.304668" y="0.954068" z="1.94622"/>
<node id="{ec7fe0a0-42da-486a-a46e-6721de480bd4}" partId="{e6fced12-025d-471f-b260-6d7854280682}" radius="0.0792079" x="0.406507" y="0.494165" z="1.7201"/> <node id="{ec7fe0a0-42da-486a-a46e-6721de480bd4}" partId="{57a41558-46e2-48ce-9f6b-54ada6b2d083}" radius="0.0792079" x="0.406507" y="0.494165" z="1.7201"/>
<node id="{fc6e43f7-ba5d-4aec-8307-c2cba3b60d25}" partId="{a8703d0e-1581-495a-99e0-4507178bf1fa}" radius="0.0118812" x="0.330693" y="0.407921" z="1.88119"/> <node id="{fc6e43f7-ba5d-4aec-8307-c2cba3b60d25}" partId="{3719b667-4110-499d-b78b-4f2ec4e66213}" radius="0.0118812" x="0.330693" y="0.407921" z="1.88119"/>
<node id="{fc9412df-c285-4fca-bd36-75e6b5fa9a10}" partId="{e6fced12-025d-471f-b260-6d7854280682}" radius="0.0118812" x="0.362942" y="0.83538" z="1.67522"/> <node id="{fc9412df-c285-4fca-bd36-75e6b5fa9a10}" partId="{57a41558-46e2-48ce-9f6b-54ada6b2d083}" radius="0.0118812" x="0.362942" y="0.83538" z="1.67522"/>
<node id="{fce1e549-0427-4875-800c-6177d0c6d0c1}" partId="{e84c3063-265f-4850-9e19-465b84e41118}" radius="0.00594059" x="0.390099" y="0.264697" z="0.887594"/> <node id="{fce1e549-0427-4875-800c-6177d0c6d0c1}" partId="{8689ab0b-3a56-4f01-b72a-0eee032928d4}" radius="0.00594059" x="0.390099" y="0.264697" z="0.887594"/>
<node id="{fe1bb602-5c84-4e9f-89e6-2fc993fc3263}" partId="{6df97180-d57c-46b3-8f96-4173b69abbc5}" radius="0.019802" x="0.380198" y="0.188474" z="0.940183"/> <node id="{fe1bb602-5c84-4e9f-89e6-2fc993fc3263}" partId="{0b0f5c6e-94d4-4c8e-9997-052a70fa3720}" radius="0.019802" x="0.380198" y="0.188474" z="0.940183"/>
</nodes> </nodes>
<edges> <edges>
<edge from="{355833f3-bd2d-4508-9293-dbf33c65222b}" id="{0571dd67-b846-4ecc-9011-5eedaaff0865}" partId="{a8703d0e-1581-495a-99e0-4507178bf1fa}" to="{2498311b-1a9b-4768-bd9c-e108e4f7a1c8}"/> <edge from="{355833f3-bd2d-4508-9293-dbf33c65222b}" id="{0571dd67-b846-4ecc-9011-5eedaaff0865}" partId="{3719b667-4110-499d-b78b-4f2ec4e66213}" to="{2498311b-1a9b-4768-bd9c-e108e4f7a1c8}"/>
<edge from="{c03f9808-ad09-4e34-8683-225be168d659}" id="{0b5bdca7-b1f3-4ca1-810f-9304ade0c583}" partId="{c5bd1431-d740-4266-825d-217513f1a1f5}" to="{7d3f47b9-2de0-46e2-9691-3c7de959777f}"/> <edge from="{c03f9808-ad09-4e34-8683-225be168d659}" id="{0b5bdca7-b1f3-4ca1-810f-9304ade0c583}" partId="{20fce58c-660f-4e93-b97a-a316d0753042}" to="{7d3f47b9-2de0-46e2-9691-3c7de959777f}"/>
<edge from="{72a946b4-b2a0-426a-884b-5cf5176f9ece}" id="{0ccf11ec-8354-47a0-b907-528675c72b35}" partId="{47d784c2-d5dc-402d-9848-e80e1c03416e}" to="{dd3e9c97-c506-4faf-9a47-d0003c62cf33}"/> <edge from="{72a946b4-b2a0-426a-884b-5cf5176f9ece}" id="{0ccf11ec-8354-47a0-b907-528675c72b35}" partId="{76e8af23-9880-458a-8218-ebf61c0ba341}" to="{dd3e9c97-c506-4faf-9a47-d0003c62cf33}"/>
<edge from="{4123d281-63d6-44a5-bdfd-62261ab8dad9}" id="{0d024d6e-4265-4168-8e70-b10effd903c0}" partId="{c722bb89-16f8-4866-b115-74c321caf271}" to="{ab7d093a-d8de-4328-97fa-d9ed37aec364}"/> <edge from="{4123d281-63d6-44a5-bdfd-62261ab8dad9}" id="{0d024d6e-4265-4168-8e70-b10effd903c0}" partId="{37860592-2626-4c6d-8069-349954bda284}" to="{ab7d093a-d8de-4328-97fa-d9ed37aec364}"/>
<edge from="{dd3e9c97-c506-4faf-9a47-d0003c62cf33}" id="{0d6cd4aa-7f87-4b3b-906c-d2292662143e}" partId="{47d784c2-d5dc-402d-9848-e80e1c03416e}" to="{22f090e3-7748-4a48-841a-925de92cce58}"/> <edge from="{dd3e9c97-c506-4faf-9a47-d0003c62cf33}" id="{0d6cd4aa-7f87-4b3b-906c-d2292662143e}" partId="{76e8af23-9880-458a-8218-ebf61c0ba341}" to="{22f090e3-7748-4a48-841a-925de92cce58}"/>
<edge from="{74399604-82fe-4cc0-a61f-0de6f297f80b}" id="{0e138337-1ae6-4b93-8321-31d1351a396f}" partId="{e6fced12-025d-471f-b260-6d7854280682}" to="{40529c5a-bafd-4bc1-84c3-daae168d5da8}"/> <edge from="{74399604-82fe-4cc0-a61f-0de6f297f80b}" id="{0e138337-1ae6-4b93-8321-31d1351a396f}" partId="{57a41558-46e2-48ce-9f6b-54ada6b2d083}" to="{40529c5a-bafd-4bc1-84c3-daae168d5da8}"/>
<edge from="{c6ab722d-0df4-46df-a666-c08534fa785e}" id="{0e31bac7-c1a3-4aba-88f0-9a269091a488}" partId="{e6fced12-025d-471f-b260-6d7854280682}" to="{2e02275f-8aa2-46f3-ad34-579a63890b17}"/> <edge from="{c6ab722d-0df4-46df-a666-c08534fa785e}" id="{0e31bac7-c1a3-4aba-88f0-9a269091a488}" partId="{57a41558-46e2-48ce-9f6b-54ada6b2d083}" to="{2e02275f-8aa2-46f3-ad34-579a63890b17}"/>
<edge from="{31ccee3b-ba23-4294-ae81-6f6b34f1ed5d}" id="{10bec875-0bb4-4840-bc88-9c48b7f6c3d6}" partId="{6df97180-d57c-46b3-8f96-4173b69abbc5}" to="{68fee6f0-597d-4079-a61b-4def65e1c8f9}"/> <edge from="{31ccee3b-ba23-4294-ae81-6f6b34f1ed5d}" id="{10bec875-0bb4-4840-bc88-9c48b7f6c3d6}" partId="{0b0f5c6e-94d4-4c8e-9997-052a70fa3720}" to="{68fee6f0-597d-4079-a61b-4def65e1c8f9}"/>
<edge from="{b6d23fe7-3bd6-4a4f-9601-0661304b1c94}" id="{1304817d-5b36-454c-9006-6c295ac5a4fc}" partId="{47d784c2-d5dc-402d-9848-e80e1c03416e}" to="{6b0da1e9-337b-4cf4-9d08-c2ab19b18c80}"/> <edge from="{b6d23fe7-3bd6-4a4f-9601-0661304b1c94}" id="{1304817d-5b36-454c-9006-6c295ac5a4fc}" partId="{76e8af23-9880-458a-8218-ebf61c0ba341}" to="{6b0da1e9-337b-4cf4-9d08-c2ab19b18c80}"/>
<edge from="{68fee6f0-597d-4079-a61b-4def65e1c8f9}" id="{14a673b6-75ca-4d94-aebb-d34dc05b6b40}" partId="{6df97180-d57c-46b3-8f96-4173b69abbc5}" to="{84c39fc7-a579-453f-b2d2-3872af219174}"/> <edge from="{68fee6f0-597d-4079-a61b-4def65e1c8f9}" id="{14a673b6-75ca-4d94-aebb-d34dc05b6b40}" partId="{0b0f5c6e-94d4-4c8e-9997-052a70fa3720}" to="{84c39fc7-a579-453f-b2d2-3872af219174}"/>
<edge from="{37dc112b-cfb9-4a87-9b82-90a27c9d1518}" id="{165d9cb1-a82d-4bbc-8884-f45fc723503b}" partId="{0df76de9-fa79-477d-9771-b85378446b3c}" to="{89836264-59bd-420a-84d1-dae0a7d98f27}"/> <edge from="{37dc112b-cfb9-4a87-9b82-90a27c9d1518}" id="{165d9cb1-a82d-4bbc-8884-f45fc723503b}" partId="{449d0e15-066f-4780-918a-55978d46ed9d}" to="{89836264-59bd-420a-84d1-dae0a7d98f27}"/>
<edge from="{8ae179bd-6309-4cfa-b892-5a040d1b75df}" id="{17dcd894-c09e-4946-8191-45e100ec4e45}" partId="{c722bb89-16f8-4866-b115-74c321caf271}" to="{259237a6-bac4-4a79-aa57-a74d357feff2}"/> <edge from="{8ae179bd-6309-4cfa-b892-5a040d1b75df}" id="{17dcd894-c09e-4946-8191-45e100ec4e45}" partId="{37860592-2626-4c6d-8069-349954bda284}" to="{259237a6-bac4-4a79-aa57-a74d357feff2}"/>
<edge from="{5b25a3a7-ed5a-42ef-a277-6fd8406649f2}" id="{18c55d57-cc36-4f4c-9fa9-b45cf7b288df}" partId="{6df97180-d57c-46b3-8f96-4173b69abbc5}" to="{fe1bb602-5c84-4e9f-89e6-2fc993fc3263}"/> <edge from="{5b25a3a7-ed5a-42ef-a277-6fd8406649f2}" id="{18c55d57-cc36-4f4c-9fa9-b45cf7b288df}" partId="{0b0f5c6e-94d4-4c8e-9997-052a70fa3720}" to="{fe1bb602-5c84-4e9f-89e6-2fc993fc3263}"/>
<edge from="{e5361db3-58e4-41af-a955-55010430d3a7}" id="{1e0ba45a-bb1b-4383-a8d0-704920602cc6}" partId="{e6fced12-025d-471f-b260-6d7854280682}" to="{74399604-82fe-4cc0-a61f-0de6f297f80b}"/> <edge from="{e5361db3-58e4-41af-a955-55010430d3a7}" id="{1e0ba45a-bb1b-4383-a8d0-704920602cc6}" partId="{57a41558-46e2-48ce-9f6b-54ada6b2d083}" to="{74399604-82fe-4cc0-a61f-0de6f297f80b}"/>
<edge from="{3e13f116-1dd9-4024-8b1b-764c176a7835}" id="{26479dee-1e07-4917-ba2a-9b9fc3ec2f71}" partId="{a8703d0e-1581-495a-99e0-4507178bf1fa}" to="{6b2b333d-2abf-4d6e-8957-7a848c46e651}"/> <edge from="{3e13f116-1dd9-4024-8b1b-764c176a7835}" id="{26479dee-1e07-4917-ba2a-9b9fc3ec2f71}" partId="{3719b667-4110-499d-b78b-4f2ec4e66213}" to="{6b2b333d-2abf-4d6e-8957-7a848c46e651}"/>
<edge from="{d71693ae-f238-4bd3-881e-bcdee119c22c}" id="{2c88fbbd-5dfd-435f-a88f-b9a8b0d9d803}" partId="{c5bd1431-d740-4266-825d-217513f1a1f5}" to="{a30b2cb5-7893-4db3-8fe8-269d30bca548}"/> <edge from="{d71693ae-f238-4bd3-881e-bcdee119c22c}" id="{2c88fbbd-5dfd-435f-a88f-b9a8b0d9d803}" partId="{20fce58c-660f-4e93-b97a-a316d0753042}" to="{a30b2cb5-7893-4db3-8fe8-269d30bca548}"/>
<edge from="{89836264-59bd-420a-84d1-dae0a7d98f27}" id="{3235421c-0321-4505-bc89-7ef5fb1884ed}" partId="{0df76de9-fa79-477d-9771-b85378446b3c}" to="{cda8ff54-1879-450f-8af9-a118ff6bfd7f}"/> <edge from="{89836264-59bd-420a-84d1-dae0a7d98f27}" id="{3235421c-0321-4505-bc89-7ef5fb1884ed}" partId="{449d0e15-066f-4780-918a-55978d46ed9d}" to="{cda8ff54-1879-450f-8af9-a118ff6bfd7f}"/>
<edge from="{40529c5a-bafd-4bc1-84c3-daae168d5da8}" id="{34e70dac-c738-4d1a-b259-bf01b26c913b}" partId="{e6fced12-025d-471f-b260-6d7854280682}" to="{fc9412df-c285-4fca-bd36-75e6b5fa9a10}"/> <edge from="{40529c5a-bafd-4bc1-84c3-daae168d5da8}" id="{34e70dac-c738-4d1a-b259-bf01b26c913b}" partId="{57a41558-46e2-48ce-9f6b-54ada6b2d083}" to="{fc9412df-c285-4fca-bd36-75e6b5fa9a10}"/>
<edge from="{ded04835-2d99-46a0-8763-7869526dd8be}" id="{38f2e48b-4df7-45b0-bdc9-231ea61d6ec3}" partId="{92839eb7-0da2-4905-ae58-a1bb3903f444}" to="{8a9ec61d-2d9c-46f1-a0da-d04b5ee65085}"/> <edge from="{ded04835-2d99-46a0-8763-7869526dd8be}" id="{38f2e48b-4df7-45b0-bdc9-231ea61d6ec3}" partId="{0a233c0a-8ce5-42d3-b720-fb97b63d0e58}" to="{8a9ec61d-2d9c-46f1-a0da-d04b5ee65085}"/>
<edge from="{e5d65034-602c-4336-a919-b4a8ff8fc68f}" id="{406ac7e5-450d-4162-8eb7-70e4d86bc14b}" partId="{0df76de9-fa79-477d-9771-b85378446b3c}" to="{021f7f20-d1c4-4b79-94ef-25ecf621ee0c}"/> <edge from="{e5d65034-602c-4336-a919-b4a8ff8fc68f}" id="{406ac7e5-450d-4162-8eb7-70e4d86bc14b}" partId="{449d0e15-066f-4780-918a-55978d46ed9d}" to="{021f7f20-d1c4-4b79-94ef-25ecf621ee0c}"/>
<edge from="{fc6e43f7-ba5d-4aec-8307-c2cba3b60d25}" id="{4211f57d-43d9-405c-872f-f184f701b2f6}" partId="{a8703d0e-1581-495a-99e0-4507178bf1fa}" to="{3e13f116-1dd9-4024-8b1b-764c176a7835}"/> <edge from="{fc6e43f7-ba5d-4aec-8307-c2cba3b60d25}" id="{4211f57d-43d9-405c-872f-f184f701b2f6}" partId="{3719b667-4110-499d-b78b-4f2ec4e66213}" to="{3e13f116-1dd9-4024-8b1b-764c176a7835}"/>
<edge from="{4c9ba403-391c-4903-a8ba-0ff250713286}" id="{4b984aed-5a41-4d31-ad6e-d8000f5aa5dc}" partId="{c5bd1431-d740-4266-825d-217513f1a1f5}" to="{c41b7308-14bf-4950-b2ba-2775199a92e0}"/> <edge from="{4c9ba403-391c-4903-a8ba-0ff250713286}" id="{4b984aed-5a41-4d31-ad6e-d8000f5aa5dc}" partId="{20fce58c-660f-4e93-b97a-a316d0753042}" to="{c41b7308-14bf-4950-b2ba-2775199a92e0}"/>
<edge from="{bcb2d6a7-7a55-424a-84a9-ef7ba7e42731}" id="{511eab57-7312-425c-96c1-c48e4aea3f1d}" partId="{6df97180-d57c-46b3-8f96-4173b69abbc5}" to="{d6fd2f1a-7c90-4c62-82be-1eeb781c5847}"/> <edge from="{bcb2d6a7-7a55-424a-84a9-ef7ba7e42731}" id="{511eab57-7312-425c-96c1-c48e4aea3f1d}" partId="{0b0f5c6e-94d4-4c8e-9997-052a70fa3720}" to="{d6fd2f1a-7c90-4c62-82be-1eeb781c5847}"/>
<edge from="{c0000c88-edd1-46ca-b57b-9dc6874f72eb}" id="{53999bf4-3333-47d0-9288-0a1d2854ddbb}" partId="{6df97180-d57c-46b3-8f96-4173b69abbc5}" to="{a459f13d-414a-454f-8739-97ddba88d0ad}"/> <edge from="{c0000c88-edd1-46ca-b57b-9dc6874f72eb}" id="{53999bf4-3333-47d0-9288-0a1d2854ddbb}" partId="{0b0f5c6e-94d4-4c8e-9997-052a70fa3720}" to="{a459f13d-414a-454f-8739-97ddba88d0ad}"/>
<edge from="{b10279cc-27c0-470f-8614-72a45ad74fb2}" id="{547bfc25-9d06-43af-bb18-87bf486ab0c6}" partId="{c722bb89-16f8-4866-b115-74c321caf271}" to="{4123d281-63d6-44a5-bdfd-62261ab8dad9}"/> <edge from="{b10279cc-27c0-470f-8614-72a45ad74fb2}" id="{547bfc25-9d06-43af-bb18-87bf486ab0c6}" partId="{37860592-2626-4c6d-8069-349954bda284}" to="{4123d281-63d6-44a5-bdfd-62261ab8dad9}"/>
<edge from="{1ccfc908-f650-44bc-b072-6d100315b927}" id="{58c2ae11-7c73-4660-8c72-09a124c8d299}" partId="{92839eb7-0da2-4905-ae58-a1bb3903f444}" to="{54127252-0ecb-4a08-84b8-489ce894aebe}"/> <edge from="{1ccfc908-f650-44bc-b072-6d100315b927}" id="{58c2ae11-7c73-4660-8c72-09a124c8d299}" partId="{0a233c0a-8ce5-42d3-b720-fb97b63d0e58}" to="{54127252-0ecb-4a08-84b8-489ce894aebe}"/>
<edge from="{ab7d093a-d8de-4328-97fa-d9ed37aec364}" id="{6795486b-b073-4971-81a4-3e370af49840}" partId="{c722bb89-16f8-4866-b115-74c321caf271}" to="{8ae179bd-6309-4cfa-b892-5a040d1b75df}"/> <edge from="{ab7d093a-d8de-4328-97fa-d9ed37aec364}" id="{6795486b-b073-4971-81a4-3e370af49840}" partId="{37860592-2626-4c6d-8069-349954bda284}" to="{8ae179bd-6309-4cfa-b892-5a040d1b75df}"/>
<edge from="{fc9412df-c285-4fca-bd36-75e6b5fa9a10}" id="{67e435e7-126a-44d2-a882-82793a9144f8}" partId="{e6fced12-025d-471f-b260-6d7854280682}" to="{c6ab722d-0df4-46df-a666-c08534fa785e}"/> <edge from="{fc9412df-c285-4fca-bd36-75e6b5fa9a10}" id="{67e435e7-126a-44d2-a882-82793a9144f8}" partId="{57a41558-46e2-48ce-9f6b-54ada6b2d083}" to="{c6ab722d-0df4-46df-a666-c08534fa785e}"/>
<edge from="{05a4fc6a-8e6f-4c0d-a6ca-023ef4bd8d14}" id="{6f2e6906-ac73-4767-9148-1d426c1223c6}" partId="{e84c3063-265f-4850-9e19-465b84e41118}" to="{6183e755-7d06-4e96-aee0-2e4a5df4c83f}"/> <edge from="{05a4fc6a-8e6f-4c0d-a6ca-023ef4bd8d14}" id="{6f2e6906-ac73-4767-9148-1d426c1223c6}" partId="{8689ab0b-3a56-4f01-b72a-0eee032928d4}" to="{6183e755-7d06-4e96-aee0-2e4a5df4c83f}"/>
<edge from="{2bcb3563-c4d3-40bb-8e49-3afe61da902f}" id="{70e4682b-c179-4c34-a6d9-1c3f5bcbb34d}" partId="{a8703d0e-1581-495a-99e0-4507178bf1fa}" to="{e274e844-fbf1-47fb-bbec-0504785f3c11}"/> <edge from="{2bcb3563-c4d3-40bb-8e49-3afe61da902f}" id="{70e4682b-c179-4c34-a6d9-1c3f5bcbb34d}" partId="{3719b667-4110-499d-b78b-4f2ec4e66213}" to="{e274e844-fbf1-47fb-bbec-0504785f3c11}"/>
<edge from="{8a9ec61d-2d9c-46f1-a0da-d04b5ee65085}" id="{762ee8d5-5fe7-40a4-aa79-e6a53566e747}" partId="{92839eb7-0da2-4905-ae58-a1bb3903f444}" to="{a2b78111-e6f4-4389-b7ed-4ba0a55599e8}"/> <edge from="{8a9ec61d-2d9c-46f1-a0da-d04b5ee65085}" id="{762ee8d5-5fe7-40a4-aa79-e6a53566e747}" partId="{0a233c0a-8ce5-42d3-b720-fb97b63d0e58}" to="{a2b78111-e6f4-4389-b7ed-4ba0a55599e8}"/>
<edge from="{913b6427-0ac2-4ace-ae9d-10434c1b5bdf}" id="{789cc205-b308-472a-9dd4-f5014f302812}" partId="{0df76de9-fa79-477d-9771-b85378446b3c}" to="{2dd69178-ccf9-4026-9319-9aebbd4155df}"/> <edge from="{913b6427-0ac2-4ace-ae9d-10434c1b5bdf}" id="{789cc205-b308-472a-9dd4-f5014f302812}" partId="{449d0e15-066f-4780-918a-55978d46ed9d}" to="{2dd69178-ccf9-4026-9319-9aebbd4155df}"/>
<edge from="{b7c80ca9-246f-4b6f-84cb-c3642691cf76}" id="{79b9c36d-cdcb-4052-a970-2bad1dd1eee2}" partId="{c722bb89-16f8-4866-b115-74c321caf271}" to="{59c85ebe-b47b-4d4b-b40a-9bb0ed434914}"/> <edge from="{b7c80ca9-246f-4b6f-84cb-c3642691cf76}" id="{79b9c36d-cdcb-4052-a970-2bad1dd1eee2}" partId="{37860592-2626-4c6d-8069-349954bda284}" to="{59c85ebe-b47b-4d4b-b40a-9bb0ed434914}"/>
<edge from="{bba5b320-733b-44a4-a701-27f12003dde9}" id="{7bf2b03e-1f3f-4578-a7be-7ffc9faeea78}" partId="{c722bb89-16f8-4866-b115-74c321caf271}" to="{7615ca8a-d9d4-4987-84b1-7c5578ac55f0}"/> <edge from="{bba5b320-733b-44a4-a701-27f12003dde9}" id="{7bf2b03e-1f3f-4578-a7be-7ffc9faeea78}" partId="{37860592-2626-4c6d-8069-349954bda284}" to="{7615ca8a-d9d4-4987-84b1-7c5578ac55f0}"/>
<edge from="{e9fd4236-5ce0-4d2e-8868-d894967fb749}" id="{7cbde8f1-cf0e-4b9e-9e96-ee7a95bb90b7}" partId="{e6fced12-025d-471f-b260-6d7854280682}" to="{7fd2060f-8360-4fb2-a9c9-ba773bcb4fc6}"/> <edge from="{e9fd4236-5ce0-4d2e-8868-d894967fb749}" id="{7cbde8f1-cf0e-4b9e-9e96-ee7a95bb90b7}" partId="{57a41558-46e2-48ce-9f6b-54ada6b2d083}" to="{7fd2060f-8360-4fb2-a9c9-ba773bcb4fc6}"/>
<edge from="{e3deddc7-edcd-4bdf-b0bd-f2d46e8bef63}" id="{7ee05d80-10eb-4057-9899-e80d52ce50d6}" partId="{47d784c2-d5dc-402d-9848-e80e1c03416e}" to="{72a946b4-b2a0-426a-884b-5cf5176f9ece}"/> <edge from="{e3deddc7-edcd-4bdf-b0bd-f2d46e8bef63}" id="{7ee05d80-10eb-4057-9899-e80d52ce50d6}" partId="{76e8af23-9880-458a-8218-ebf61c0ba341}" to="{72a946b4-b2a0-426a-884b-5cf5176f9ece}"/>
<edge from="{e97e7112-dae7-4465-8cb2-d1d66fc81dee}" id="{7fb90996-7133-4748-b958-ecd9b5314179}" partId="{c722bb89-16f8-4866-b115-74c321caf271}" to="{25b71535-a196-49b7-a019-ae08cc75dd60}"/> <edge from="{e97e7112-dae7-4465-8cb2-d1d66fc81dee}" id="{7fb90996-7133-4748-b958-ecd9b5314179}" partId="{37860592-2626-4c6d-8069-349954bda284}" to="{25b71535-a196-49b7-a019-ae08cc75dd60}"/>
<edge from="{1e3e9eb9-ab4c-4b64-97ad-4342d24b02f9}" id="{80ef73f7-7dfa-4bde-808c-b18b4219cf0d}" partId="{c5bd1431-d740-4266-825d-217513f1a1f5}" to="{5526b16b-07a8-44ae-a2a1-23edd995a34e}"/> <edge from="{1e3e9eb9-ab4c-4b64-97ad-4342d24b02f9}" id="{80ef73f7-7dfa-4bde-808c-b18b4219cf0d}" partId="{20fce58c-660f-4e93-b97a-a316d0753042}" to="{5526b16b-07a8-44ae-a2a1-23edd995a34e}"/>
<edge from="{0da7cc8e-dd93-4b86-a305-fe14ae189790}" id="{818c8cca-41c6-4305-90d1-1afa2e52b509}" partId="{c722bb89-16f8-4866-b115-74c321caf271}" to="{bba5b320-733b-44a4-a701-27f12003dde9}"/> <edge from="{0da7cc8e-dd93-4b86-a305-fe14ae189790}" id="{818c8cca-41c6-4305-90d1-1afa2e52b509}" partId="{37860592-2626-4c6d-8069-349954bda284}" to="{bba5b320-733b-44a4-a701-27f12003dde9}"/>
<edge from="{1a732d47-0948-439d-a173-d0129af7644d}" id="{81e64481-ea3f-4ef6-b235-179cc699cfd1}" partId="{a8703d0e-1581-495a-99e0-4507178bf1fa}" to="{2bcb3563-c4d3-40bb-8e49-3afe61da902f}"/> <edge from="{1a732d47-0948-439d-a173-d0129af7644d}" id="{81e64481-ea3f-4ef6-b235-179cc699cfd1}" partId="{3719b667-4110-499d-b78b-4f2ec4e66213}" to="{2bcb3563-c4d3-40bb-8e49-3afe61da902f}"/>
<edge from="{b88113fa-d829-40e0-95ef-0462032ce7b9}" id="{825aa883-9f21-4f59-a825-5e6d1b85c360}" partId="{e6fced12-025d-471f-b260-6d7854280682}" to="{e5361db3-58e4-41af-a955-55010430d3a7}"/> <edge from="{b88113fa-d829-40e0-95ef-0462032ce7b9}" id="{825aa883-9f21-4f59-a825-5e6d1b85c360}" partId="{57a41558-46e2-48ce-9f6b-54ada6b2d083}" to="{e5361db3-58e4-41af-a955-55010430d3a7}"/>
<edge from="{4ff2a8f5-aade-4320-98c5-f78abbf31df0}" id="{8271cc73-9e77-4b65-9192-d025f8db480a}" partId="{0df76de9-fa79-477d-9771-b85378446b3c}" to="{9ba73d77-9c23-40eb-97c3-b6d6c8ebd6d9}"/> <edge from="{4ff2a8f5-aade-4320-98c5-f78abbf31df0}" id="{8271cc73-9e77-4b65-9192-d025f8db480a}" partId="{449d0e15-066f-4780-918a-55978d46ed9d}" to="{9ba73d77-9c23-40eb-97c3-b6d6c8ebd6d9}"/>
<edge from="{7fd2060f-8360-4fb2-a9c9-ba773bcb4fc6}" id="{8357f57c-1e39-4641-92cf-715097acd4d2}" partId="{e6fced12-025d-471f-b260-6d7854280682}" to="{baae591e-9437-454a-ba87-ea28bbde87c9}"/> <edge from="{7fd2060f-8360-4fb2-a9c9-ba773bcb4fc6}" id="{8357f57c-1e39-4641-92cf-715097acd4d2}" partId="{57a41558-46e2-48ce-9f6b-54ada6b2d083}" to="{baae591e-9437-454a-ba87-ea28bbde87c9}"/>
<edge from="{883859a6-0102-4a26-8596-3c47bcaf1970}" id="{83ab6636-39af-445e-a558-8524daa3457e}" partId="{a8703d0e-1581-495a-99e0-4507178bf1fa}" to="{355833f3-bd2d-4508-9293-dbf33c65222b}"/> <edge from="{883859a6-0102-4a26-8596-3c47bcaf1970}" id="{83ab6636-39af-445e-a558-8524daa3457e}" partId="{3719b667-4110-499d-b78b-4f2ec4e66213}" to="{355833f3-bd2d-4508-9293-dbf33c65222b}"/>
<edge from="{06b524b3-b2e5-4e12-993f-497c57aee71e}" id="{8c355a03-2920-42a1-8aeb-3ac4518a14d7}" partId="{0df76de9-fa79-477d-9771-b85378446b3c}" to="{37dc112b-cfb9-4a87-9b82-90a27c9d1518}"/> <edge from="{06b524b3-b2e5-4e12-993f-497c57aee71e}" id="{8c355a03-2920-42a1-8aeb-3ac4518a14d7}" partId="{449d0e15-066f-4780-918a-55978d46ed9d}" to="{37dc112b-cfb9-4a87-9b82-90a27c9d1518}"/>
<edge from="{54127252-0ecb-4a08-84b8-489ce894aebe}" id="{8eee20ef-8b62-468c-891a-48b0da139bda}" partId="{92839eb7-0da2-4905-ae58-a1bb3903f444}" to="{936ea324-7ae0-4fe3-be1c-bc3f43bbcd7b}"/> <edge from="{54127252-0ecb-4a08-84b8-489ce894aebe}" id="{8eee20ef-8b62-468c-891a-48b0da139bda}" partId="{0a233c0a-8ce5-42d3-b720-fb97b63d0e58}" to="{936ea324-7ae0-4fe3-be1c-bc3f43bbcd7b}"/>
<edge from="{7d3f47b9-2de0-46e2-9691-3c7de959777f}" id="{92b04bef-2f8a-4ef0-bba0-52ca298b5515}" partId="{c5bd1431-d740-4266-825d-217513f1a1f5}" to="{1e3e9eb9-ab4c-4b64-97ad-4342d24b02f9}"/> <edge from="{7d3f47b9-2de0-46e2-9691-3c7de959777f}" id="{92b04bef-2f8a-4ef0-bba0-52ca298b5515}" partId="{20fce58c-660f-4e93-b97a-a316d0753042}" to="{1e3e9eb9-ab4c-4b64-97ad-4342d24b02f9}"/>
<edge from="{d46924c8-54d7-4f2b-8376-b5e12a7b535c}" id="{9a89b123-3117-4abd-bf09-556ca6ed70e1}" partId="{92839eb7-0da2-4905-ae58-a1bb3903f444}" to="{ded04835-2d99-46a0-8763-7869526dd8be}"/> <edge from="{d46924c8-54d7-4f2b-8376-b5e12a7b535c}" id="{9a89b123-3117-4abd-bf09-556ca6ed70e1}" partId="{0a233c0a-8ce5-42d3-b720-fb97b63d0e58}" to="{ded04835-2d99-46a0-8763-7869526dd8be}"/>
<edge from="{7f6a90b8-57d3-4319-8bd3-c7c1733f52f5}" id="{9b481d72-e16b-4561-8a61-0c9eedd4195c}" partId="{0df76de9-fa79-477d-9771-b85378446b3c}" to="{67afd8c7-012e-4db6-8de5-b2cb35cfbc50}"/> <edge from="{7f6a90b8-57d3-4319-8bd3-c7c1733f52f5}" id="{9b481d72-e16b-4561-8a61-0c9eedd4195c}" partId="{449d0e15-066f-4780-918a-55978d46ed9d}" to="{67afd8c7-012e-4db6-8de5-b2cb35cfbc50}"/>
<edge from="{2498311b-1a9b-4768-bd9c-e108e4f7a1c8}" id="{9b9f6939-d9c5-495e-832b-6c05563d15c6}" partId="{a8703d0e-1581-495a-99e0-4507178bf1fa}" to="{5f73c8dd-e8c1-484c-af35-5b2789c0582c}"/> <edge from="{2498311b-1a9b-4768-bd9c-e108e4f7a1c8}" id="{9b9f6939-d9c5-495e-832b-6c05563d15c6}" partId="{3719b667-4110-499d-b78b-4f2ec4e66213}" to="{5f73c8dd-e8c1-484c-af35-5b2789c0582c}"/>
<edge from="{2e02275f-8aa2-46f3-ad34-579a63890b17}" id="{9f6e8cb5-6ca9-43cc-90a1-8777c6bc3f4d}" partId="{e6fced12-025d-471f-b260-6d7854280682}" to="{e9fd4236-5ce0-4d2e-8868-d894967fb749}"/> <edge from="{2e02275f-8aa2-46f3-ad34-579a63890b17}" id="{9f6e8cb5-6ca9-43cc-90a1-8777c6bc3f4d}" partId="{57a41558-46e2-48ce-9f6b-54ada6b2d083}" to="{e9fd4236-5ce0-4d2e-8868-d894967fb749}"/>
<edge from="{b0e81284-8fbc-4757-a52e-c485fe57d92c}" id="{a0d3799c-5771-40ea-a082-16eb44484097}" partId="{92839eb7-0da2-4905-ae58-a1bb3903f444}" to="{8da76f01-7c78-4227-a64e-af317cb591d5}"/> <edge from="{b0e81284-8fbc-4757-a52e-c485fe57d92c}" id="{a0d3799c-5771-40ea-a082-16eb44484097}" partId="{0a233c0a-8ce5-42d3-b720-fb97b63d0e58}" to="{8da76f01-7c78-4227-a64e-af317cb591d5}"/>
<edge from="{ec7fe0a0-42da-486a-a46e-6721de480bd4}" id="{a414e67e-32aa-469a-aa0b-6bc9c017914f}" partId="{e6fced12-025d-471f-b260-6d7854280682}" to="{e40576eb-3d06-4c53-8f29-98683f2d7aab}"/> <edge from="{ec7fe0a0-42da-486a-a46e-6721de480bd4}" id="{a414e67e-32aa-469a-aa0b-6bc9c017914f}" partId="{57a41558-46e2-48ce-9f6b-54ada6b2d083}" to="{e40576eb-3d06-4c53-8f29-98683f2d7aab}"/>
<edge from="{6b0da1e9-337b-4cf4-9d08-c2ab19b18c80}" id="{a6bdf02e-8a58-412b-b966-4d7ab5a75c4c}" partId="{47d784c2-d5dc-402d-9848-e80e1c03416e}" to="{e3deddc7-edcd-4bdf-b0bd-f2d46e8bef63}"/> <edge from="{6b0da1e9-337b-4cf4-9d08-c2ab19b18c80}" id="{a6bdf02e-8a58-412b-b966-4d7ab5a75c4c}" partId="{76e8af23-9880-458a-8218-ebf61c0ba341}" to="{e3deddc7-edcd-4bdf-b0bd-f2d46e8bef63}"/>
<edge from="{2dd69178-ccf9-4026-9319-9aebbd4155df}" id="{a8ec6007-46c5-469e-a2a7-8f9e07600769}" partId="{0df76de9-fa79-477d-9771-b85378446b3c}" to="{7af1da13-55ff-4ce7-9fcd-ab9825098cb8}"/> <edge from="{2dd69178-ccf9-4026-9319-9aebbd4155df}" id="{a8ec6007-46c5-469e-a2a7-8f9e07600769}" partId="{449d0e15-066f-4780-918a-55978d46ed9d}" to="{7af1da13-55ff-4ce7-9fcd-ab9825098cb8}"/>
<edge from="{eb75e8d6-9b28-4ec5-afa6-59b006152425}" id="{abeca8a9-6bd4-4b0a-8251-e96bf407048b}" partId="{92839eb7-0da2-4905-ae58-a1bb3903f444}" to="{2ea7a440-8ff5-4143-ab72-ebe178087607}"/> <edge from="{eb75e8d6-9b28-4ec5-afa6-59b006152425}" id="{abeca8a9-6bd4-4b0a-8251-e96bf407048b}" partId="{0a233c0a-8ce5-42d3-b720-fb97b63d0e58}" to="{2ea7a440-8ff5-4143-ab72-ebe178087607}"/>
<edge from="{936ea324-7ae0-4fe3-be1c-bc3f43bbcd7b}" id="{ac022191-87c3-4c7a-a438-116cbbdda637}" partId="{92839eb7-0da2-4905-ae58-a1bb3903f444}" to="{b7527147-9500-4627-8704-5e363c3a2779}"/> <edge from="{936ea324-7ae0-4fe3-be1c-bc3f43bbcd7b}" id="{ac022191-87c3-4c7a-a438-116cbbdda637}" partId="{0a233c0a-8ce5-42d3-b720-fb97b63d0e58}" to="{b7527147-9500-4627-8704-5e363c3a2779}"/>
<edge from="{e274e844-fbf1-47fb-bbec-0504785f3c11}" id="{acf93680-3ad2-401b-b262-c62f7e474f67}" partId="{a8703d0e-1581-495a-99e0-4507178bf1fa}" to="{6c08eb60-5120-4ae2-9854-df02e5ebf9c8}"/> <edge from="{e274e844-fbf1-47fb-bbec-0504785f3c11}" id="{acf93680-3ad2-401b-b262-c62f7e474f67}" partId="{3719b667-4110-499d-b78b-4f2ec4e66213}" to="{6c08eb60-5120-4ae2-9854-df02e5ebf9c8}"/>
<edge from="{4d605b53-37e2-4622-a9df-b457e4b1ae89}" id="{ae82ab35-71da-41c1-8910-04e89d1eb5e9}" partId="{e6fced12-025d-471f-b260-6d7854280682}" to="{ec7fe0a0-42da-486a-a46e-6721de480bd4}"/> <edge from="{4d605b53-37e2-4622-a9df-b457e4b1ae89}" id="{ae82ab35-71da-41c1-8910-04e89d1eb5e9}" partId="{57a41558-46e2-48ce-9f6b-54ada6b2d083}" to="{ec7fe0a0-42da-486a-a46e-6721de480bd4}"/>
<edge from="{05a4fc6a-8e6f-4c0d-a6ca-023ef4bd8d14}" id="{aeb759e3-fa06-494a-8750-51bbf9cd4686}" partId="{e84c3063-265f-4850-9e19-465b84e41118}" to="{fce1e549-0427-4875-800c-6177d0c6d0c1}"/> <edge from="{05a4fc6a-8e6f-4c0d-a6ca-023ef4bd8d14}" id="{aeb759e3-fa06-494a-8750-51bbf9cd4686}" partId="{8689ab0b-3a56-4f01-b72a-0eee032928d4}" to="{fce1e549-0427-4875-800c-6177d0c6d0c1}"/>
<edge from="{180bd087-9aa6-487a-8b50-5f2231539863}" id="{b27a7b36-cd01-4c1d-8498-86160dfc104e}" partId="{92839eb7-0da2-4905-ae58-a1bb3903f444}" to="{bff67e53-359b-4e50-bafb-dcc9dd84f4ec}"/> <edge from="{180bd087-9aa6-487a-8b50-5f2231539863}" id="{b27a7b36-cd01-4c1d-8498-86160dfc104e}" partId="{0a233c0a-8ce5-42d3-b720-fb97b63d0e58}" to="{bff67e53-359b-4e50-bafb-dcc9dd84f4ec}"/>
<edge from="{d884eda9-29ca-41ab-8a45-3995c8058549}" id="{b28f37c3-86c7-41cc-bf50-1abfdd047230}" partId="{e6fced12-025d-471f-b260-6d7854280682}" to="{b88113fa-d829-40e0-95ef-0462032ce7b9}"/> <edge from="{d884eda9-29ca-41ab-8a45-3995c8058549}" id="{b28f37c3-86c7-41cc-bf50-1abfdd047230}" partId="{57a41558-46e2-48ce-9f6b-54ada6b2d083}" to="{b88113fa-d829-40e0-95ef-0462032ce7b9}"/>
<edge from="{fe1bb602-5c84-4e9f-89e6-2fc993fc3263}" id="{b571a73e-6106-4568-aac8-89a04bfd56d0}" partId="{6df97180-d57c-46b3-8f96-4173b69abbc5}" to="{31ccee3b-ba23-4294-ae81-6f6b34f1ed5d}"/> <edge from="{fe1bb602-5c84-4e9f-89e6-2fc993fc3263}" id="{b571a73e-6106-4568-aac8-89a04bfd56d0}" partId="{0b0f5c6e-94d4-4c8e-9997-052a70fa3720}" to="{31ccee3b-ba23-4294-ae81-6f6b34f1ed5d}"/>
<edge from="{350d2902-c60e-4cba-a10c-d000ecba47ad}" id="{b75865bf-917f-4555-9089-3337cb56a5a4}" partId="{c5bd1431-d740-4266-825d-217513f1a1f5}" to="{d71693ae-f238-4bd3-881e-bcdee119c22c}"/> <edge from="{350d2902-c60e-4cba-a10c-d000ecba47ad}" id="{b75865bf-917f-4555-9089-3337cb56a5a4}" partId="{20fce58c-660f-4e93-b97a-a316d0753042}" to="{d71693ae-f238-4bd3-881e-bcdee119c22c}"/>
<edge from="{6b2b333d-2abf-4d6e-8957-7a848c46e651}" id="{b8b4db97-0620-4f9e-ad9b-febcd68bc3a2}" partId="{a8703d0e-1581-495a-99e0-4507178bf1fa}" to="{7cfbf2a4-3d92-4357-8644-4e195dfac673}"/> <edge from="{6b2b333d-2abf-4d6e-8957-7a848c46e651}" id="{b8b4db97-0620-4f9e-ad9b-febcd68bc3a2}" partId="{3719b667-4110-499d-b78b-4f2ec4e66213}" to="{7cfbf2a4-3d92-4357-8644-4e195dfac673}"/>
<edge from="{8da76f01-7c78-4227-a64e-af317cb591d5}" id="{ba3022d3-0ac2-4b38-bac2-40d43337ade9}" partId="{92839eb7-0da2-4905-ae58-a1bb3903f444}" to="{d46924c8-54d7-4f2b-8376-b5e12a7b535c}"/> <edge from="{8da76f01-7c78-4227-a64e-af317cb591d5}" id="{ba3022d3-0ac2-4b38-bac2-40d43337ade9}" partId="{0a233c0a-8ce5-42d3-b720-fb97b63d0e58}" to="{d46924c8-54d7-4f2b-8376-b5e12a7b535c}"/>
<edge from="{e40576eb-3d06-4c53-8f29-98683f2d7aab}" id="{bc36eb75-79af-4854-90a8-40cf4ce00ef2}" partId="{e6fced12-025d-471f-b260-6d7854280682}" to="{d884eda9-29ca-41ab-8a45-3995c8058549}"/> <edge from="{e40576eb-3d06-4c53-8f29-98683f2d7aab}" id="{bc36eb75-79af-4854-90a8-40cf4ce00ef2}" partId="{57a41558-46e2-48ce-9f6b-54ada6b2d083}" to="{d884eda9-29ca-41ab-8a45-3995c8058549}"/>
<edge from="{d6fd2f1a-7c90-4c62-82be-1eeb781c5847}" id="{be5ad665-f8a3-4759-a292-5c7ce7c36379}" partId="{6df97180-d57c-46b3-8f96-4173b69abbc5}" to="{c0000c88-edd1-46ca-b57b-9dc6874f72eb}"/> <edge from="{d6fd2f1a-7c90-4c62-82be-1eeb781c5847}" id="{be5ad665-f8a3-4759-a292-5c7ce7c36379}" partId="{0b0f5c6e-94d4-4c8e-9997-052a70fa3720}" to="{c0000c88-edd1-46ca-b57b-9dc6874f72eb}"/>
<edge from="{6c08eb60-5120-4ae2-9854-df02e5ebf9c8}" id="{c0dcff73-1887-4b09-b84b-8c2d73f7ba88}" partId="{a8703d0e-1581-495a-99e0-4507178bf1fa}" to="{fc6e43f7-ba5d-4aec-8307-c2cba3b60d25}"/> <edge from="{6c08eb60-5120-4ae2-9854-df02e5ebf9c8}" id="{c0dcff73-1887-4b09-b84b-8c2d73f7ba88}" partId="{3719b667-4110-499d-b78b-4f2ec4e66213}" to="{fc6e43f7-ba5d-4aec-8307-c2cba3b60d25}"/>
<edge from="{b7527147-9500-4627-8704-5e363c3a2779}" id="{c3d0ce6d-956a-4400-b24b-aae9864b13fb}" partId="{92839eb7-0da2-4905-ae58-a1bb3903f444}" to="{180bd087-9aa6-487a-8b50-5f2231539863}"/> <edge from="{b7527147-9500-4627-8704-5e363c3a2779}" id="{c3d0ce6d-956a-4400-b24b-aae9864b13fb}" partId="{0a233c0a-8ce5-42d3-b720-fb97b63d0e58}" to="{180bd087-9aa6-487a-8b50-5f2231539863}"/>
<edge from="{cda8ff54-1879-450f-8af9-a118ff6bfd7f}" id="{c6f54f48-0c1b-4ced-96c2-0643d36f751d}" partId="{0df76de9-fa79-477d-9771-b85378446b3c}" to="{4ff2a8f5-aade-4320-98c5-f78abbf31df0}"/> <edge from="{cda8ff54-1879-450f-8af9-a118ff6bfd7f}" id="{c6f54f48-0c1b-4ced-96c2-0643d36f751d}" partId="{449d0e15-066f-4780-918a-55978d46ed9d}" to="{4ff2a8f5-aade-4320-98c5-f78abbf31df0}"/>
<edge from="{59c85ebe-b47b-4d4b-b40a-9bb0ed434914}" id="{c9bec503-da68-4899-b137-05802372eb5d}" partId="{c722bb89-16f8-4866-b115-74c321caf271}" to="{e97e7112-dae7-4465-8cb2-d1d66fc81dee}"/> <edge from="{59c85ebe-b47b-4d4b-b40a-9bb0ed434914}" id="{c9bec503-da68-4899-b137-05802372eb5d}" partId="{37860592-2626-4c6d-8069-349954bda284}" to="{e97e7112-dae7-4465-8cb2-d1d66fc81dee}"/>
<edge from="{a2b78111-e6f4-4389-b7ed-4ba0a55599e8}" id="{c9c18f11-be89-4d07-bd5d-bc61b240f6e3}" partId="{92839eb7-0da2-4905-ae58-a1bb3903f444}" to="{eb75e8d6-9b28-4ec5-afa6-59b006152425}"/> <edge from="{a2b78111-e6f4-4389-b7ed-4ba0a55599e8}" id="{c9c18f11-be89-4d07-bd5d-bc61b240f6e3}" partId="{0a233c0a-8ce5-42d3-b720-fb97b63d0e58}" to="{eb75e8d6-9b28-4ec5-afa6-59b006152425}"/>
<edge from="{5526b16b-07a8-44ae-a2a1-23edd995a34e}" id="{cffc683c-b686-456e-8256-9715cc3af11c}" partId="{c5bd1431-d740-4266-825d-217513f1a1f5}" to="{4ab72941-0a5e-442f-919f-aff60341427b}"/> <edge from="{5526b16b-07a8-44ae-a2a1-23edd995a34e}" id="{cffc683c-b686-456e-8256-9715cc3af11c}" partId="{20fce58c-660f-4e93-b97a-a316d0753042}" to="{4ab72941-0a5e-442f-919f-aff60341427b}"/>
<edge from="{ca57c536-2786-4b36-a3d9-9590b9658843}" id="{d2d11141-a91c-4c23-a406-b2702e56aa2e}" partId="{c5bd1431-d740-4266-825d-217513f1a1f5}" to="{c03f9808-ad09-4e34-8683-225be168d659}"/> <edge from="{ca57c536-2786-4b36-a3d9-9590b9658843}" id="{d2d11141-a91c-4c23-a406-b2702e56aa2e}" partId="{20fce58c-660f-4e93-b97a-a316d0753042}" to="{c03f9808-ad09-4e34-8683-225be168d659}"/>
<edge from="{c41b7308-14bf-4950-b2ba-2775199a92e0}" id="{d3cbd72b-29d6-4e5a-990e-60308c700dcb}" partId="{c5bd1431-d740-4266-825d-217513f1a1f5}" to="{ca57c536-2786-4b36-a3d9-9590b9658843}"/> <edge from="{c41b7308-14bf-4950-b2ba-2775199a92e0}" id="{d3cbd72b-29d6-4e5a-990e-60308c700dcb}" partId="{20fce58c-660f-4e93-b97a-a316d0753042}" to="{ca57c536-2786-4b36-a3d9-9590b9658843}"/>
<edge from="{03ff6913-45f1-4508-a39f-3842694cb9f7}" id="{d8cea11e-3a2f-4665-aba1-c2f735fafe4f}" partId="{c722bb89-16f8-4866-b115-74c321caf271}" to="{b7c80ca9-246f-4b6f-84cb-c3642691cf76}"/> <edge from="{03ff6913-45f1-4508-a39f-3842694cb9f7}" id="{d8cea11e-3a2f-4665-aba1-c2f735fafe4f}" partId="{37860592-2626-4c6d-8069-349954bda284}" to="{b7c80ca9-246f-4b6f-84cb-c3642691cf76}"/>
<edge from="{021f7f20-d1c4-4b79-94ef-25ecf621ee0c}" id="{e1295617-c398-44dd-9f3e-3642bd5b4fd0}" partId="{0df76de9-fa79-477d-9771-b85378446b3c}" to="{06b524b3-b2e5-4e12-993f-497c57aee71e}"/> <edge from="{021f7f20-d1c4-4b79-94ef-25ecf621ee0c}" id="{e1295617-c398-44dd-9f3e-3642bd5b4fd0}" partId="{449d0e15-066f-4780-918a-55978d46ed9d}" to="{06b524b3-b2e5-4e12-993f-497c57aee71e}"/>
<edge from="{bff67e53-359b-4e50-bafb-dcc9dd84f4ec}" id="{e909cd44-983d-48f6-84be-b72a9da90e23}" partId="{92839eb7-0da2-4905-ae58-a1bb3903f444}" to="{b0e81284-8fbc-4757-a52e-c485fe57d92c}"/> <edge from="{bff67e53-359b-4e50-bafb-dcc9dd84f4ec}" id="{e909cd44-983d-48f6-84be-b72a9da90e23}" partId="{0a233c0a-8ce5-42d3-b720-fb97b63d0e58}" to="{b0e81284-8fbc-4757-a52e-c485fe57d92c}"/>
<edge from="{6281b6cb-d0ae-43b2-b124-26fcedacf472}" id="{ea5ff8b9-9072-4597-a0be-590bda826036}" partId="{c5bd1431-d740-4266-825d-217513f1a1f5}" to="{71ea7967-b59e-4507-bf55-225882ecbba5}"/> <edge from="{6281b6cb-d0ae-43b2-b124-26fcedacf472}" id="{ea5ff8b9-9072-4597-a0be-590bda826036}" partId="{20fce58c-660f-4e93-b97a-a316d0753042}" to="{71ea7967-b59e-4507-bf55-225882ecbba5}"/>
<edge from="{7af1da13-55ff-4ce7-9fcd-ab9825098cb8}" id="{ed5751b8-c5c9-45fb-9d36-59bf1acd46ba}" partId="{0df76de9-fa79-477d-9771-b85378446b3c}" to="{e5d65034-602c-4336-a919-b4a8ff8fc68f}"/> <edge from="{7af1da13-55ff-4ce7-9fcd-ab9825098cb8}" id="{ed5751b8-c5c9-45fb-9d36-59bf1acd46ba}" partId="{449d0e15-066f-4780-918a-55978d46ed9d}" to="{e5d65034-602c-4336-a919-b4a8ff8fc68f}"/>
<edge from="{d32e752f-fdd0-4ccc-8558-f341ceef5f91}" id="{ed7f4d40-55fb-4905-8e21-e1926963be4a}" partId="{6df97180-d57c-46b3-8f96-4173b69abbc5}" to="{797ac170-8b69-423e-8ca5-acb7217188be}"/> <edge from="{d32e752f-fdd0-4ccc-8558-f341ceef5f91}" id="{ed7f4d40-55fb-4905-8e21-e1926963be4a}" partId="{0b0f5c6e-94d4-4c8e-9997-052a70fa3720}" to="{797ac170-8b69-423e-8ca5-acb7217188be}"/>
<edge from="{9ba73d77-9c23-40eb-97c3-b6d6c8ebd6d9}" id="{ee60e02e-74aa-4225-a2ce-e5c7640810b8}" partId="{0df76de9-fa79-477d-9771-b85378446b3c}" to="{7f6a90b8-57d3-4319-8bd3-c7c1733f52f5}"/> <edge from="{9ba73d77-9c23-40eb-97c3-b6d6c8ebd6d9}" id="{ee60e02e-74aa-4225-a2ce-e5c7640810b8}" partId="{449d0e15-066f-4780-918a-55978d46ed9d}" to="{7f6a90b8-57d3-4319-8bd3-c7c1733f52f5}"/>
<edge from="{c6050850-281f-41f3-b736-c734179996c3}" id="{ef205344-30a5-4074-be1d-c84101ed974d}" partId="{6df97180-d57c-46b3-8f96-4173b69abbc5}" to="{d32e752f-fdd0-4ccc-8558-f341ceef5f91}"/> <edge from="{c6050850-281f-41f3-b736-c734179996c3}" id="{ef205344-30a5-4074-be1d-c84101ed974d}" partId="{0b0f5c6e-94d4-4c8e-9997-052a70fa3720}" to="{d32e752f-fdd0-4ccc-8558-f341ceef5f91}"/>
<edge from="{71ea7967-b59e-4507-bf55-225882ecbba5}" id="{f345a9c6-b8cf-47b6-85fb-b8e2453e4ba2}" partId="{c5bd1431-d740-4266-825d-217513f1a1f5}" to="{4c9ba403-391c-4903-a8ba-0ff250713286}"/> <edge from="{71ea7967-b59e-4507-bf55-225882ecbba5}" id="{f345a9c6-b8cf-47b6-85fb-b8e2453e4ba2}" partId="{20fce58c-660f-4e93-b97a-a316d0753042}" to="{4c9ba403-391c-4903-a8ba-0ff250713286}"/>
<edge from="{84c39fc7-a579-453f-b2d2-3872af219174}" id="{f66a4dd5-23ae-4b98-94ac-516c55097579}" partId="{6df97180-d57c-46b3-8f96-4173b69abbc5}" to="{bcb2d6a7-7a55-424a-84a9-ef7ba7e42731}"/> <edge from="{84c39fc7-a579-453f-b2d2-3872af219174}" id="{f66a4dd5-23ae-4b98-94ac-516c55097579}" partId="{0b0f5c6e-94d4-4c8e-9997-052a70fa3720}" to="{bcb2d6a7-7a55-424a-84a9-ef7ba7e42731}"/>
<edge from="{7615ca8a-d9d4-4987-84b1-7c5578ac55f0}" id="{f6792241-1cb0-4dca-83f5-196104a5e084}" partId="{c722bb89-16f8-4866-b115-74c321caf271}" to="{b10279cc-27c0-470f-8614-72a45ad74fb2}"/> <edge from="{7615ca8a-d9d4-4987-84b1-7c5578ac55f0}" id="{f6792241-1cb0-4dca-83f5-196104a5e084}" partId="{37860592-2626-4c6d-8069-349954bda284}" to="{b10279cc-27c0-470f-8614-72a45ad74fb2}"/>
<edge from="{7cfbf2a4-3d92-4357-8644-4e195dfac673}" id="{f94b58a6-5da2-41cc-9d4b-fd636f830ac3}" partId="{a8703d0e-1581-495a-99e0-4507178bf1fa}" to="{883859a6-0102-4a26-8596-3c47bcaf1970}"/> <edge from="{7cfbf2a4-3d92-4357-8644-4e195dfac673}" id="{f94b58a6-5da2-41cc-9d4b-fd636f830ac3}" partId="{3719b667-4110-499d-b78b-4f2ec4e66213}" to="{883859a6-0102-4a26-8596-3c47bcaf1970}"/>
<edge from="{4ab72941-0a5e-442f-919f-aff60341427b}" id="{fa298da1-3ba9-4a3c-9b10-48592e3adecf}" partId="{c5bd1431-d740-4266-825d-217513f1a1f5}" to="{350d2902-c60e-4cba-a10c-d000ecba47ad}"/> <edge from="{4ab72941-0a5e-442f-919f-aff60341427b}" id="{fa298da1-3ba9-4a3c-9b10-48592e3adecf}" partId="{20fce58c-660f-4e93-b97a-a316d0753042}" to="{350d2902-c60e-4cba-a10c-d000ecba47ad}"/>
<edge from="{25b71535-a196-49b7-a019-ae08cc75dd60}" id="{fc3465c1-148c-4933-b1d3-fd3779f5a79c}" partId="{c722bb89-16f8-4866-b115-74c321caf271}" to="{0da7cc8e-dd93-4b86-a305-fe14ae189790}"/> <edge from="{25b71535-a196-49b7-a019-ae08cc75dd60}" id="{fc3465c1-148c-4933-b1d3-fd3779f5a79c}" partId="{37860592-2626-4c6d-8069-349954bda284}" to="{0da7cc8e-dd93-4b86-a305-fe14ae189790}"/>
<edge from="{259237a6-bac4-4a79-aa57-a74d357feff2}" id="{fc8d90fd-2e16-44e5-9aef-95836cb57abe}" partId="{c722bb89-16f8-4866-b115-74c321caf271}" to="{734b73e6-9175-42d8-8ffe-057b933bc666}"/> <edge from="{259237a6-bac4-4a79-aa57-a74d357feff2}" id="{fc8d90fd-2e16-44e5-9aef-95836cb57abe}" partId="{37860592-2626-4c6d-8069-349954bda284}" to="{734b73e6-9175-42d8-8ffe-057b933bc666}"/>
<edge from="{dd48d6f8-0172-4886-a11a-6c9f36444be5}" id="{fd138d2f-058c-44af-950d-6c8d8ff02597}" partId="{c5bd1431-d740-4266-825d-217513f1a1f5}" to="{6281b6cb-d0ae-43b2-b124-26fcedacf472}"/> <edge from="{dd48d6f8-0172-4886-a11a-6c9f36444be5}" id="{fd138d2f-058c-44af-950d-6c8d8ff02597}" partId="{20fce58c-660f-4e93-b97a-a316d0753042}" to="{6281b6cb-d0ae-43b2-b124-26fcedacf472}"/>
<edge from="{a459f13d-414a-454f-8739-97ddba88d0ad}" id="{fdebed2f-479c-46eb-90a3-9c450383dba6}" partId="{6df97180-d57c-46b3-8f96-4173b69abbc5}" to="{c6050850-281f-41f3-b736-c734179996c3}"/> <edge from="{a459f13d-414a-454f-8739-97ddba88d0ad}" id="{fdebed2f-479c-46eb-90a3-9c450383dba6}" partId="{0b0f5c6e-94d4-4c8e-9997-052a70fa3720}" to="{c6050850-281f-41f3-b736-c734179996c3}"/>
</edges> </edges>
<parts> <parts>
<part chamfered="false" color="#ffe0d6cc" countershaded="true" deformThickness="0.9" disabled="false" id="{0df76de9-fa79-477d-9771-b85378446b3c}" locked="false" rounded="true" subdived="true" visible="true" xMirrored="false"/> <part chamfered="false" color="#ffe0d6cc" deformWidth="0.84" disabled="false" id="{0a233c0a-8ce5-42d3-b720-fb97b63d0e58}" locked="false" rounded="true" subdived="true" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ffe0d6cc" disabled="false" id="{47d784c2-d5dc-402d-9848-e80e1c03416e}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="true"/> <part chamfered="false" color="#ffa19274" deformWidth="0.78" disabled="false" id="{0b0f5c6e-94d4-4c8e-9997-052a70fa3720}" locked="false" rounded="true" subdived="false" visible="true" xMirrored="true"/>
<part chamfered="false" color="#ffa19274" deformWidth="0.78" disabled="false" id="{6df97180-d57c-46b3-8f96-4173b69abbc5}" locked="false" rounded="true" subdived="false" visible="true" xMirrored="true"/> <part chamfered="false" color="#ffe0d6cc" deformWidth="0.78" disabled="false" id="{20fce58c-660f-4e93-b97a-a316d0753042}" locked="false" rounded="true" subdived="true" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ffe0d6cc" deformWidth="0.84" disabled="false" id="{92839eb7-0da2-4905-ae58-a1bb3903f444}" locked="false" rounded="true" subdived="true" visible="true" xMirrored="false"/> <part chamfered="false" color="#ffe0d6cc" disabled="false" id="{3719b667-4110-499d-b78b-4f2ec4e66213}" locked="false" rounded="true" subdived="false" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ffe0d6cc" disabled="false" id="{a8703d0e-1581-495a-99e0-4507178bf1fa}" locked="false" rounded="true" subdived="false" visible="true" xMirrored="false"/> <part base="YZ" chamfered="false" color="#ffe0d6cc" deformThickness="0.67" disabled="false" id="{37860592-2626-4c6d-8069-349954bda284}" locked="false" rounded="true" subdived="true" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ffe0d6cc" deformWidth="0.78" disabled="false" id="{c5bd1431-d740-4266-825d-217513f1a1f5}" locked="false" rounded="true" subdived="true" visible="true" xMirrored="false"/> <part chamfered="false" color="#ffe0d6cc" countershaded="true" deformThickness="0.9" disabled="false" id="{449d0e15-066f-4780-918a-55978d46ed9d}" locked="false" rounded="true" subdived="true" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ffe0d6cc" deformThickness="0.67" disabled="false" id="{c722bb89-16f8-4866-b115-74c321caf271}" locked="false" rounded="true" subdived="true" visible="true" xMirrored="false"/> <part chamfered="false" color="#ffe0d6cc" deformWidth="0.84" disabled="false" id="{57a41558-46e2-48ce-9f6b-54ada6b2d083}" locked="false" rounded="true" subdived="true" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ffe0d6cc" deformWidth="0.84" disabled="false" id="{e6fced12-025d-471f-b260-6d7854280682}" locked="false" rounded="true" subdived="true" visible="true" xMirrored="false"/> <part chamfered="false" color="#ffe0d6cc" disabled="false" id="{76e8af23-9880-458a-8218-ebf61c0ba341}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="true"/>
<part chamfered="false" color="#ff675e4c" disabled="false" id="{e84c3063-265f-4850-9e19-465b84e41118}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="true"/> <part chamfered="false" color="#ff675e4c" disabled="false" id="{8689ab0b-3a56-4f01-b72a-0eee032928d4}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="true"/>
</parts> </parts>
<components> <components>
<component combineMode="Normal" expanded="false" id="{ff328100-3815-472c-80ba-0aeed815ba5f}" linkData="{0df76de9-fa79-477d-9771-b85378446b3c}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{19e6614b-a56f-49fa-9387-8b3d49379d95}" linkData="{449d0e15-066f-4780-918a-55978d46ed9d}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{b5bf0500-8c5a-4033-990c-4fe46ba37210}" linkData="{6df97180-d57c-46b3-8f96-4173b69abbc5}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{6093e8bc-9a95-4f30-ac61-b6b84a117a61}" linkData="{0b0f5c6e-94d4-4c8e-9997-052a70fa3720}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{9bca00cb-3f47-4675-a94f-23b243ba31f8}" linkData="{a8703d0e-1581-495a-99e0-4507178bf1fa}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{33a056aa-596d-49a0-b9b0-168b3245c3bd}" linkData="{3719b667-4110-499d-b78b-4f2ec4e66213}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{5f6bab49-2b71-4216-ab35-6b4354b00d01}" linkData="{c722bb89-16f8-4866-b115-74c321caf271}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{1d8b664b-651f-49b3-8210-db55dc5021be}" linkData="{37860592-2626-4c6d-8069-349954bda284}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{b1737790-b19e-4165-bcf4-faa061a6e7ba}" linkData="{c5bd1431-d740-4266-825d-217513f1a1f5}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{070e4698-1cdf-454e-91d9-c8ffcf775e80}" linkData="{20fce58c-660f-4e93-b97a-a316d0753042}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{ef2e2f92-6443-4836-9b87-4a812814a68f}" linkData="{47d784c2-d5dc-402d-9848-e80e1c03416e}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{6b029d28-9104-406c-a737-a5c554de71b1}" linkData="{76e8af23-9880-458a-8218-ebf61c0ba341}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{4f104e42-4d09-4efb-ab6b-61739b831588}" linkData="{92839eb7-0da2-4905-ae58-a1bb3903f444}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{cc087c8e-ef7d-454f-806b-3e1c35099274}" linkData="{0a233c0a-8ce5-42d3-b720-fb97b63d0e58}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{1c8d6af8-a290-452e-9e87-cd401f0d8e81}" linkData="{e6fced12-025d-471f-b260-6d7854280682}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{0d66852f-0a5b-4a53-8b20-ff0d46829e51}" linkData="{57a41558-46e2-48ce-9f6b-54ada6b2d083}" linkDataType="partId"/>
<component combineMode="Inversion" expanded="false" id="{29da2789-fc49-4eb9-93df-f92b395394e3}" linkData="{e84c3063-265f-4850-9e19-465b84e41118}" linkDataType="partId"/> <component combineMode="Inversion" expanded="false" id="{a5aa1b28-5d78-404e-b8b9-2d115a9859ee}" linkData="{8689ab0b-3a56-4f01-b72a-0eee032928d4}" linkDataType="partId"/>
</components> </components>
<materials/> <materials/>
<poses/> <poses/>

File diff suppressed because it is too large Load Diff

View File

@ -7,234 +7,234 @@ DUST3D 1.0 xml 0000000193
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<canvas originX="0.293069" originY="0.623762" originZ="1.20693" polyCount="TremendouslyHighPoly" rigType="Animal"> <canvas originX="0.293069" originY="0.623762" originZ="1.20693" polyCount="TremendouslyHighPoly" rigType="Animal">
<nodes> <nodes>
<node id="{00da2bf5-53b1-495b-8f56-aaf78e74e9c2}" partId="{57622c57-5929-4a6a-b4ac-17dc909f002a}" radius="0.019802" x="0.293069" y="0.849505" z="1.45743"/> <node id="{00da2bf5-53b1-495b-8f56-aaf78e74e9c2}" partId="{0a57548e-12d8-4488-9ce4-8fe3b64db253}" radius="0.019802" x="0.293069" y="0.849505" z="1.45743"/>
<node id="{0369aab4-b9b7-4d53-bc43-118f226dadc9}" partId="{019249f8-0f48-4f9e-87e3-6d2e821e87ac}" radius="0.00594061" x="0.10792" y="0.940163" z="1.2098"/> <node id="{0369aab4-b9b7-4d53-bc43-118f226dadc9}" partId="{65aa6ccd-94db-47cf-aa84-dce3c2b3fa65}" radius="0.00594061" x="0.10792" y="0.940163" z="1.2098"/>
<node id="{05872b14-1e9a-4ba9-bbf8-0111c9100527}" partId="{dd604661-6acc-4ef1-83fc-36a374c54734}" radius="0.0118812" x="0.368776" y="0.121915" z="1.19315"/> <node id="{05872b14-1e9a-4ba9-bbf8-0111c9100527}" partId="{ff29229a-564e-4e50-af37-0ae6c25b78ae}" radius="0.0118812" x="0.368776" y="0.121915" z="1.19315"/>
<node id="{0891b311-79de-4ed7-8410-b3e43e675a9b}" partId="{ad2259a2-362c-499e-9123-d6f8ac0a1966}" radius="0.0910891" x="0.29505" y="0.382178" z="1.17822"/> <node id="{0891b311-79de-4ed7-8410-b3e43e675a9b}" partId="{422ad92f-8ad5-402a-9629-2348eb14e103}" radius="0.0910891" x="0.29505" y="0.382178" z="1.17822"/>
<node boneMark="Limb" id="{08f7ae7b-869a-4f40-87a4-a575d78c2a60}" partId="{b27111c8-625f-405d-88a9-55df58d51de0}" radius="0.0594059" x="0.427115" y="0.655402" z="1.22574"/> <node boneMark="Limb" id="{08f7ae7b-869a-4f40-87a4-a575d78c2a60}" partId="{5f080e1f-48f5-4aa7-aa0c-d175f9045341}" radius="0.0594059" x="0.427115" y="0.655402" z="1.22574"/>
<node id="{0cecd379-b9d6-4568-9d5b-82720916a6e4}" partId="{0b8f8da9-d461-48c4-bbfd-343f0b648235}" radius="0.0118812" x="0.459895" y="0.514042" z="1.06634"/> <node id="{0cecd379-b9d6-4568-9d5b-82720916a6e4}" partId="{0048f320-55a9-4777-914e-95944cb34b46}" radius="0.0118812" x="0.459895" y="0.514042" z="1.06634"/>
<node boneMark="Joint" id="{13bc43e1-b35e-4645-8fd5-e9e8b16d5fc8}" partId="{881a0adc-3b0d-4575-b7d7-d6df8907e3f3}" radius="0.0594059" x="0.128121" y="0.759157" z="1.18614"/> <node boneMark="Joint" id="{13bc43e1-b35e-4645-8fd5-e9e8b16d5fc8}" partId="{79289522-791a-488f-bd2d-15db6c8f2f84}" radius="0.0594059" x="0.128121" y="0.759157" z="1.18614"/>
<node id="{13d47f89-649c-4830-ba6d-fbf7f132793e}" partId="{9fae01c2-855a-472b-8181-02a1b76fcab1}" radius="0.0079208" x="0.13613" y="0.495533" z="1.0901"/> <node id="{13d47f89-649c-4830-ba6d-fbf7f132793e}" partId="{b6e20100-8c2b-4550-aee8-806aa713c90c}" radius="0.0079208" x="0.13613" y="0.495533" z="1.0901"/>
<node id="{1e7e802c-3711-40c8-9dd6-bcded439cffe}" partId="{a5ad3f64-2091-4cea-8085-e17881f0573d}" radius="0.0079208" x="0.468977" y="0.966347" z="1.19721"/> <node id="{1e7e802c-3711-40c8-9dd6-bcded439cffe}" partId="{cef2d5ce-ed55-4030-a415-e39a20d3ff86}" radius="0.0079208" x="0.468977" y="0.966347" z="1.19721"/>
<node id="{2073cdb1-77ef-4c51-8ca0-4ad4e0908d6c}" partId="{b27111c8-625f-405d-88a9-55df58d51de0}" radius="0.019802" x="0.454833" y="0.953807" z="1.2099"/> <node id="{2073cdb1-77ef-4c51-8ca0-4ad4e0908d6c}" partId="{5f080e1f-48f5-4aa7-aa0c-d175f9045341}" radius="0.019802" x="0.454833" y="0.953807" z="1.2099"/>
<node boneMark="Limb" id="{22f4fc91-c657-497f-aef8-87d38a42e23f}" partId="{d5ccb7d6-3357-473c-a26a-12ce5713d92e}" radius="0.0316832" x="0.165992" y="0.30808" z="1.19516"/> <node boneMark="Limb" id="{22f4fc91-c657-497f-aef8-87d38a42e23f}" partId="{9c9383da-b1ad-4fed-ac7d-e65d9334882f}" radius="0.0316832" x="0.165992" y="0.30808" z="1.19516"/>
<node boneMark="Joint" id="{25b52681-7ee3-458e-8db2-bf784e1c101d}" partId="{b27111c8-625f-405d-88a9-55df58d51de0}" radius="0.019802" x="0.398712" y="0.903059" z="1.23366"/> <node boneMark="Joint" id="{25b52681-7ee3-458e-8db2-bf784e1c101d}" partId="{5f080e1f-48f5-4aa7-aa0c-d175f9045341}" radius="0.019802" x="0.398712" y="0.903059" z="1.23366"/>
<node id="{25e5be7a-dbb8-4d8f-a0b7-441eef6c694f}" partId="{d5ccb7d6-3357-473c-a26a-12ce5713d92e}" radius="0.039604" x="0.259919" y="0.25653" z="1.19061"/> <node id="{25e5be7a-dbb8-4d8f-a0b7-441eef6c694f}" partId="{9c9383da-b1ad-4fed-ac7d-e65d9334882f}" radius="0.039604" x="0.259919" y="0.25653" z="1.19061"/>
<node id="{273c09b7-d466-496f-9d06-ef7fa267c9a7}" partId="{c9c2c2b9-571b-4067-8b01-b59725670dd4}" radius="0.0079208" x="0.473093" y="0.513442" z="1.06634"/> <node id="{273c09b7-d466-496f-9d06-ef7fa267c9a7}" partId="{c569a92e-6b1b-4d8b-9284-76fef9c124f0}" radius="0.0079208" x="0.473093" y="0.513442" z="1.06634"/>
<node id="{274f069c-1c6b-4837-8892-853ca652c07e}" partId="{471dfce0-6e13-4146-b261-2090e30f407b}" radius="0.0079208" x="0.468977" y="0.963426" z="1.1784"/> <node id="{274f069c-1c6b-4837-8892-853ca652c07e}" partId="{aceff516-7672-4ab0-8de4-10fb8c7a0681}" radius="0.0079208" x="0.468977" y="0.963426" z="1.1784"/>
<node id="{27721191-b625-44bd-9253-cfc7df7b094b}" partId="{dd604661-6acc-4ef1-83fc-36a374c54734}" radius="0.0158416" x="0.378718" y="0.105351" z="1.20042"/> <node id="{27721191-b625-44bd-9253-cfc7df7b094b}" partId="{ff29229a-564e-4e50-af37-0ae6c25b78ae}" radius="0.0158416" x="0.378718" y="0.105351" z="1.20042"/>
<node id="{27fb4823-e857-4e61-a454-a12d30ecfb17}" partId="{a5ad3f64-2091-4cea-8085-e17881f0573d}" radius="0.005" x="0.474917" y="0.981778" z="1.19311"/> <node id="{27fb4823-e857-4e61-a454-a12d30ecfb17}" partId="{cef2d5ce-ed55-4030-a415-e39a20d3ff86}" radius="0.005" x="0.474917" y="0.981778" z="1.19311"/>
<node id="{286523f2-bf39-4f2c-aae7-dcf7223140e4}" partId="{442a221d-5a04-4d09-9cfd-bdd138320c26}" radius="0.005" x="0.0801996" y="0.968697" z="1.17343"/> <node id="{286523f2-bf39-4f2c-aae7-dcf7223140e4}" partId="{de6790b2-a8be-42ea-9f96-75c8ac21c6da}" radius="0.005" x="0.0801996" y="0.968697" z="1.17343"/>
<node id="{2b5dae5c-17e3-4632-ab54-36e21859fbca}" partId="{ad2259a2-362c-499e-9123-d6f8ac0a1966}" radius="0.0871287" x="0.29505" y="0.287129" z="1.17624"/> <node id="{2b5dae5c-17e3-4632-ab54-36e21859fbca}" partId="{422ad92f-8ad5-402a-9629-2348eb14e103}" radius="0.0871287" x="0.29505" y="0.287129" z="1.17624"/>
<node id="{2bec4749-11de-442a-862f-8d04d0c590ef}" partId="{ad2259a2-362c-499e-9123-d6f8ac0a1966}" radius="0.0831683" x="0.29505" y="0.766337" z="1.23564"/> <node id="{2bec4749-11de-442a-862f-8d04d0c590ef}" partId="{422ad92f-8ad5-402a-9629-2348eb14e103}" radius="0.0831683" x="0.29505" y="0.766337" z="1.23564"/>
<node boneMark="Joint" id="{2e54f33b-c879-4e40-83cc-75eeefaccdc3}" partId="{b27111c8-625f-405d-88a9-55df58d51de0}" radius="0.0594059" x="0.438783" y="0.770349" z="1.18812"/> <node boneMark="Joint" id="{2e54f33b-c879-4e40-83cc-75eeefaccdc3}" partId="{5f080e1f-48f5-4aa7-aa0c-d175f9045341}" radius="0.0594059" x="0.438783" y="0.770349" z="1.18812"/>
<node id="{37ad0424-3633-48a9-a2ea-bac83d140ec8}" partId="{b27111c8-625f-405d-88a9-55df58d51de0}" radius="0.0316832" x="0.428869" y="0.838801" z="1.22178"/> <node id="{37ad0424-3633-48a9-a2ea-bac83d140ec8}" partId="{5f080e1f-48f5-4aa7-aa0c-d175f9045341}" radius="0.0316832" x="0.428869" y="0.838801" z="1.22178"/>
<node id="{3c4fd98e-c829-43f2-9ae5-c3c1c064a0a8}" partId="{2dacf558-7216-4577-8631-16769f860e18}" radius="0.005" x="0.149958" y="0.521944" z="1.09604"/> <node id="{3c4fd98e-c829-43f2-9ae5-c3c1c064a0a8}" partId="{828c4638-7c11-4aeb-8f23-773dbeea8440}" radius="0.005" x="0.149958" y="0.521944" z="1.09604"/>
<node id="{3d6b06a7-af07-48e4-849b-204cb9af6a69}" partId="{881a0adc-3b0d-4575-b7d7-d6df8907e3f3}" radius="0.039604" x="0.254276" y="0.568263" z="1.20099"/> <node id="{3d6b06a7-af07-48e4-849b-204cb9af6a69}" partId="{79289522-791a-488f-bd2d-15db6c8f2f84}" radius="0.039604" x="0.254276" y="0.568263" z="1.20099"/>
<node boneMark="Limb" id="{3e59449e-b487-4708-8b3e-31242cb50d19}" partId="{881a0adc-3b0d-4575-b7d7-d6df8907e3f3}" radius="0.0594059" x="0.145463" y="0.652011" z="1.22575"/> <node boneMark="Limb" id="{3e59449e-b487-4708-8b3e-31242cb50d19}" partId="{79289522-791a-488f-bd2d-15db6c8f2f84}" radius="0.0594059" x="0.145463" y="0.652011" z="1.22575"/>
<node id="{419c8aca-4bb8-4bab-84e1-214b23a17a27}" partId="{a5ad3f64-2091-4cea-8085-e17881f0573d}" radius="0.0059406" x="0.447197" y="0.951167" z="1.20163"/> <node id="{419c8aca-4bb8-4bab-84e1-214b23a17a27}" partId="{cef2d5ce-ed55-4030-a415-e39a20d3ff86}" radius="0.0059406" x="0.447197" y="0.951167" z="1.20163"/>
<node id="{457564d3-ccd0-465a-8d0b-b57c379666fd}" partId="{57622c57-5929-4a6a-b4ac-17dc909f002a}" radius="0.019802" x="0.293069" y="0.873267" z="1.35842"/> <node id="{457564d3-ccd0-465a-8d0b-b57c379666fd}" partId="{0a57548e-12d8-4488-9ce4-8fe3b64db253}" radius="0.019802" x="0.293069" y="0.873267" z="1.35842"/>
<node id="{4946f825-7960-4653-9148-8d21e269c74e}" partId="{d5ccb7d6-3357-473c-a26a-12ce5713d92e}" radius="0.0277228" x="0.143884" y="0.352036" z="1.17367"/> <node id="{4946f825-7960-4653-9148-8d21e269c74e}" partId="{9c9383da-b1ad-4fed-ac7d-e65d9334882f}" radius="0.0277228" x="0.143884" y="0.352036" z="1.17367"/>
<node id="{4b8f93a6-db8f-47c8-8d37-777db444914c}" partId="{dd604661-6acc-4ef1-83fc-36a374c54734}" radius="0.0158416" x="0.358132" y="0.0672427" z="1.20202"/> <node id="{4b8f93a6-db8f-47c8-8d37-777db444914c}" partId="{ff29229a-564e-4e50-af37-0ae6c25b78ae}" radius="0.0158416" x="0.358132" y="0.0672427" z="1.20202"/>
<node id="{4c1029e5-c16d-442c-bc48-bdeed5d4706a}" partId="{881a0adc-3b0d-4575-b7d7-d6df8907e3f3}" radius="0.019802" x="0.109793" y="0.942925" z="1.20991"/> <node id="{4c1029e5-c16d-442c-bc48-bdeed5d4706a}" partId="{79289522-791a-488f-bd2d-15db6c8f2f84}" radius="0.019802" x="0.109793" y="0.942925" z="1.20991"/>
<node boneMark="Joint" id="{50c1e61f-d968-4d2f-b4c2-9839326f49da}" partId="{d5ccb7d6-3357-473c-a26a-12ce5713d92e}" radius="0.0277228" x="0.155635" y="0.394941" z="1.12743"/> <node boneMark="Joint" id="{50c1e61f-d968-4d2f-b4c2-9839326f49da}" partId="{9c9383da-b1ad-4fed-ac7d-e65d9334882f}" radius="0.0277228" x="0.155635" y="0.394941" z="1.12743"/>
<node id="{52a3dfd0-0bb7-4869-91b2-85c729e289b0}" partId="{9fae01c2-855a-472b-8181-02a1b76fcab1}" radius="0.0118812" x="0.151192" y="0.467655" z="1.10792"/> <node id="{52a3dfd0-0bb7-4869-91b2-85c729e289b0}" partId="{b6e20100-8c2b-4550-aee8-806aa713c90c}" radius="0.0118812" x="0.151192" y="0.467655" z="1.10792"/>
<node id="{5492a461-1b28-47c8-811c-2542022eff84}" partId="{2dacf558-7216-4577-8631-16769f860e18}" radius="0.005" x="0.148921" y="0.541719" z="1.10198"/> <node id="{5492a461-1b28-47c8-811c-2542022eff84}" partId="{828c4638-7c11-4aeb-8f23-773dbeea8440}" radius="0.005" x="0.148921" y="0.541719" z="1.10198"/>
<node id="{5bad0bb7-9015-44cc-a57f-4200b20a84f0}" partId="{9fae01c2-855a-472b-8181-02a1b76fcab1}" radius="0.005" x="0.129207" y="0.528428" z="1.09604"/> <node id="{5bad0bb7-9015-44cc-a57f-4200b20a84f0}" partId="{b6e20100-8c2b-4550-aee8-806aa713c90c}" radius="0.005" x="0.129207" y="0.528428" z="1.09604"/>
<node id="{5d85b12a-fdd1-42c7-83d4-027d77716fc8}" partId="{c9c2c2b9-571b-4067-8b01-b59725670dd4}" radius="0.0118812" x="0.458031" y="0.485563" z="1.08416"/> <node id="{5d85b12a-fdd1-42c7-83d4-027d77716fc8}" partId="{c569a92e-6b1b-4d8b-9284-76fef9c124f0}" radius="0.0118812" x="0.458031" y="0.485563" z="1.08416"/>
<node id="{5eb58123-c8a7-4899-a34e-1c63df33e759}" partId="{442a221d-5a04-4d09-9cfd-bdd138320c26}" radius="0.0079208" x="0.0861396" y="0.953524" z="1.1784"/> <node id="{5eb58123-c8a7-4899-a34e-1c63df33e759}" partId="{de6790b2-a8be-42ea-9f96-75c8ac21c6da}" radius="0.0079208" x="0.0861396" y="0.953524" z="1.1784"/>
<node id="{5ff7a560-b046-4ddd-b26e-7dfbcc1083b3}" partId="{983cf6ce-44d9-41e8-8489-289b423a423a}" radius="0.0118812" x="0.44012" y="0.515079" z="1.06634"/> <node id="{5ff7a560-b046-4ddd-b26e-7dfbcc1083b3}" partId="{1f878838-6686-4bcc-bc82-d9c5583d05e8}" radius="0.0118812" x="0.44012" y="0.515079" z="1.06634"/>
<node boneMark="Tail" id="{6186ebb3-84a4-4f2c-9992-b39a3831dea3}" partId="{57622c57-5929-4a6a-b4ac-17dc909f002a}" radius="0.0277228" x="0.293069" y="0.823762" z="1.31089"/> <node boneMark="Tail" id="{6186ebb3-84a4-4f2c-9992-b39a3831dea3}" partId="{0a57548e-12d8-4488-9ce4-8fe3b64db253}" radius="0.0277228" x="0.293069" y="0.823762" z="1.31089"/>
<node id="{653c799b-da4a-4d0a-9be5-84bbe3ae7551}" partId="{0b8f8da9-d461-48c4-bbfd-343f0b648235}" radius="0.0118812" x="0.458444" y="0.486357" z="1.08416"/> <node id="{653c799b-da4a-4d0a-9be5-84bbe3ae7551}" partId="{0048f320-55a9-4777-914e-95944cb34b46}" radius="0.0118812" x="0.458444" y="0.486357" z="1.08416"/>
<node boneMark="Joint" id="{6954e176-c739-40d8-b1ef-8ce38884bba2}" partId="{881a0adc-3b0d-4575-b7d7-d6df8907e3f3}" radius="0.019802" x="0.170124" y="0.897261" z="1.23367"/> <node boneMark="Joint" id="{6954e176-c739-40d8-b1ef-8ce38884bba2}" partId="{79289522-791a-488f-bd2d-15db6c8f2f84}" radius="0.019802" x="0.170124" y="0.897261" z="1.23367"/>
<node id="{69d7c60a-eaa7-4d64-a09b-755fb3b4c9ae}" partId="{d2be1f6f-2053-491c-962b-f27e1a809848}" radius="0.0237624" x="0.452707" y="0.453717" z="1.09675"/> <node id="{69d7c60a-eaa7-4d64-a09b-755fb3b4c9ae}" partId="{93d5b991-f821-4974-96bd-17c46090ffa9}" radius="0.0237624" x="0.452707" y="0.453717" z="1.09675"/>
<node id="{6b2d17cd-4fbe-4fd3-8f80-6c467d3811fd}" partId="{471dfce0-6e13-4146-b261-2090e30f407b}" radius="0.0079208" x="0.447197" y="0.952032" z="1.2028"/> <node id="{6b2d17cd-4fbe-4fd3-8f80-6c467d3811fd}" partId="{aceff516-7672-4ab0-8de4-10fb8c7a0681}" radius="0.0079208" x="0.447197" y="0.952032" z="1.2028"/>
<node id="{6e90e23a-c67b-4dc7-af76-2145793a9c3b}" partId="{471dfce0-6e13-4146-b261-2090e30f407b}" radius="0.005" x="0.474917" y="0.978599" z="1.17343"/> <node id="{6e90e23a-c67b-4dc7-af76-2145793a9c3b}" partId="{aceff516-7672-4ab0-8de4-10fb8c7a0681}" radius="0.005" x="0.474917" y="0.978599" z="1.17343"/>
<node id="{723a0584-b3a1-41a9-a9dc-96b21e733f1f}" partId="{ad2259a2-362c-499e-9123-d6f8ac0a1966}" radius="0.0712871" x="0.293069" y="0.174257" z="1.20099"/> <node id="{723a0584-b3a1-41a9-a9dc-96b21e733f1f}" partId="{422ad92f-8ad5-402a-9629-2348eb14e103}" radius="0.0712871" x="0.293069" y="0.174257" z="1.20099"/>
<node id="{7305f345-84d5-44b5-af37-c48bf3925ca4}" partId="{ad2259a2-362c-499e-9123-d6f8ac0a1966}" radius="0.0950495" x="0.29505" y="0.508911" z="1.19406"/> <node id="{7305f345-84d5-44b5-af37-c48bf3925ca4}" partId="{422ad92f-8ad5-402a-9629-2348eb14e103}" radius="0.0950495" x="0.29505" y="0.508911" z="1.19406"/>
<node boneMark="Neck" id="{78d00d69-b1b9-4fad-b0cc-87f9b6f1539f}" partId="{fe5e25e8-58cb-43d5-ba72-c64e2cf44d6d}" radius="0.0554455" x="0.293069" y="0.172277" z="1.20792"/> <node boneMark="Neck" id="{78d00d69-b1b9-4fad-b0cc-87f9b6f1539f}" partId="{6ff1a4e8-b864-4c5e-8a8e-2d28edd2c55a}" radius="0.0554455" x="0.293069" y="0.172277" z="1.20792"/>
<node id="{78f63ac5-8fc8-40dd-80e4-b316b3b4b409}" partId="{dd604661-6acc-4ef1-83fc-36a374c54734}" radius="0.0237624" x="0.371603" y="0.0817292" z="1.20441"/> <node id="{78f63ac5-8fc8-40dd-80e4-b316b3b4b409}" partId="{ff29229a-564e-4e50-af37-0ae6c25b78ae}" radius="0.0237624" x="0.371603" y="0.0817292" z="1.20441"/>
<node id="{79b757ee-5f39-4a71-8019-5e447daa9860}" partId="{d4c0d323-791a-4a2a-ad1b-8e8fb93bf42d}" radius="0.0059406" x="0.10792" y="0.941265" z="1.20163"/> <node id="{79b757ee-5f39-4a71-8019-5e447daa9860}" partId="{8d55eb65-52a8-47da-9b5b-7d6d914943cc}" radius="0.0059406" x="0.10792" y="0.941265" z="1.20163"/>
<node id="{83e2e87a-2fd0-4df4-8278-28dd30482f67}" partId="{983cf6ce-44d9-41e8-8489-289b423a423a}" radius="0.005" x="0.437409" y="0.539015" z="1.07228"/> <node id="{83e2e87a-2fd0-4df4-8278-28dd30482f67}" partId="{1f878838-6686-4bcc-bc82-d9c5583d05e8}" radius="0.005" x="0.437409" y="0.539015" z="1.07228"/>
<node boneMark="Joint" id="{8642cce7-c158-4559-99af-bf2caa121348}" partId="{d2be1f6f-2053-491c-962b-f27e1a809848}" radius="0.0277228" x="0.456934" y="0.413582" z="1.10575"/> <node boneMark="Joint" id="{8642cce7-c158-4559-99af-bf2caa121348}" partId="{93d5b991-f821-4974-96bd-17c46090ffa9}" radius="0.0277228" x="0.456934" y="0.413582" z="1.10575"/>
<node id="{8b71e1a2-e0bc-435d-9bd2-c096f60c7f23}" partId="{546efa0c-392d-47c5-80c1-4138b07cf0a4}" radius="0.005" x="0.170777" y="0.540882" z="1.10198"/> <node id="{8b71e1a2-e0bc-435d-9bd2-c096f60c7f23}" partId="{2162fa67-7e23-4e5f-9a9d-4175b10c44c9}" radius="0.005" x="0.170777" y="0.540882" z="1.10198"/>
<node id="{8c4b6bea-3045-4b62-ba65-442c7c5548b9}" partId="{d2be1f6f-2053-491c-962b-f27e1a809848}" radius="0.0277228" x="0.471089" y="0.374671" z="1.15476"/> <node id="{8c4b6bea-3045-4b62-ba65-442c7c5548b9}" partId="{93d5b991-f821-4974-96bd-17c46090ffa9}" radius="0.0277228" x="0.471089" y="0.374671" z="1.15476"/>
<node id="{8f14d451-9613-48ef-8670-124223236475}" partId="{91850869-d0e1-4e42-a452-d1259dbd5643}" radius="0.00792081" x="0.10792" y="0.940742" z="1.20846"/> <node id="{8f14d451-9613-48ef-8670-124223236475}" partId="{c2b415b2-4419-408e-be54-1b6b2521441a}" radius="0.00792081" x="0.10792" y="0.940742" z="1.20846"/>
<node id="{90b31f2b-f532-4402-b648-4d7717ce6853}" partId="{80e078b3-ad1d-4a0f-b695-e63bff35af45}" radius="0.00990099" x="0.364357" y="0.0435644" z="1.13564"/> <node id="{90b31f2b-f532-4402-b648-4d7717ce6853}" partId="{62dc13e0-9173-4223-9c39-c682f30a2338}" radius="0.00990099" x="0.364357" y="0.0435644" z="1.13564"/>
<node id="{944c7657-e978-4bb6-94ad-b03dbafae824}" partId="{d5ccb7d6-3357-473c-a26a-12ce5713d92e}" radius="0.0237624" x="0.1577" y="0.43577" z="1.12124"/> <node id="{944c7657-e978-4bb6-94ad-b03dbafae824}" partId="{9c9383da-b1ad-4fed-ac7d-e65d9334882f}" radius="0.0237624" x="0.1577" y="0.43577" z="1.12124"/>
<node id="{94c6dd69-3b99-4b25-b6dd-e8d80b579a7e}" partId="{2c899cf6-d358-454e-ad79-5a5bddaf0bff}" radius="0.0158416" x="0.348515" y="0.0534653" z="1.16238"/> <node id="{94c6dd69-3b99-4b25-b6dd-e8d80b579a7e}" partId="{3145e23e-d0bf-4f3f-8609-92f40c348fa5}" radius="0.0158416" x="0.348515" y="0.0534653" z="1.16238"/>
<node id="{98e623f4-0c83-4da8-9ba8-c4d470f0c5f0}" partId="{6c3c3e49-1701-4f8c-9490-32d51f52fe9f}" radius="0.0079208" x="0.468977" y="0.967235" z="1.22968"/> <node id="{98e623f4-0c83-4da8-9ba8-c4d470f0c5f0}" partId="{b049bb5c-a4e5-402e-88db-a5cccbee3851}" radius="0.0079208" x="0.468977" y="0.967235" z="1.22968"/>
<node id="{9ad35b2b-3998-4822-9ecf-ff2425531106}" partId="{b27111c8-625f-405d-88a9-55df58d51de0}" radius="0.019802" x="0.426945" y="0.930406" z="1.22376"/> <node id="{9ad35b2b-3998-4822-9ecf-ff2425531106}" partId="{5f080e1f-48f5-4aa7-aa0c-d175f9045341}" radius="0.019802" x="0.426945" y="0.930406" z="1.22376"/>
<node id="{9bc24536-52b6-42c4-801e-6994069ae93b}" partId="{91850869-d0e1-4e42-a452-d1259dbd5643}" radius="0.0079208" x="0.0861396" y="0.957333" z="1.22968"/> <node id="{9bc24536-52b6-42c4-801e-6994069ae93b}" partId="{c2b415b2-4419-408e-be54-1b6b2521441a}" radius="0.0079208" x="0.0861396" y="0.957333" z="1.22968"/>
<node id="{9d2b664d-a090-4c9c-b8a3-6a07d8d0eb08}" partId="{fe5e25e8-58cb-43d5-ba72-c64e2cf44d6d}" radius="0.0158416" x="0.293069" y="0.0613861" z="1.01287"/> <node id="{9d2b664d-a090-4c9c-b8a3-6a07d8d0eb08}" partId="{6ff1a4e8-b864-4c5e-8a8e-2d28edd2c55a}" radius="0.0158416" x="0.293069" y="0.0613861" z="1.01287"/>
<node id="{a003f447-592d-45db-ac31-50e1b0dacb02}" partId="{c9c2c2b9-571b-4067-8b01-b59725670dd4}" radius="0.005" x="0.480016" y="0.546337" z="1.07228"/> <node id="{a003f447-592d-45db-ac31-50e1b0dacb02}" partId="{c569a92e-6b1b-4d8b-9284-76fef9c124f0}" radius="0.005" x="0.480016" y="0.546337" z="1.07228"/>
<node id="{a019b2a5-f245-4139-b1a2-6b9462326c51}" partId="{983cf6ce-44d9-41e8-8489-289b423a423a}" radius="0.0118812" x="0.450534" y="0.486772" z="1.08416"/> <node id="{a019b2a5-f245-4139-b1a2-6b9462326c51}" partId="{1f878838-6686-4bcc-bc82-d9c5583d05e8}" radius="0.0118812" x="0.450534" y="0.486772" z="1.08416"/>
<node id="{a43e38c8-aa79-4a6f-860b-65055bafcf62}" partId="{91850869-d0e1-4e42-a452-d1259dbd5643}" radius="0.005" x="0.0801996" y="0.973235" z="1.2311"/> <node id="{a43e38c8-aa79-4a6f-860b-65055bafcf62}" partId="{c2b415b2-4419-408e-be54-1b6b2521441a}" radius="0.005" x="0.0801996" y="0.973235" z="1.2311"/>
<node id="{a4935618-a165-4f84-aabf-c6d317c7a714}" partId="{0b8f8da9-d461-48c4-bbfd-343f0b648235}" radius="0.005" x="0.459265" y="0.539853" z="1.07228"/> <node id="{a4935618-a165-4f84-aabf-c6d317c7a714}" partId="{0048f320-55a9-4777-914e-95944cb34b46}" radius="0.005" x="0.459265" y="0.539853" z="1.07228"/>
<node id="{a6567353-5b7a-4de0-8d0f-b916fd8a7287}" partId="{d4c0d323-791a-4a2a-ad1b-8e8fb93bf42d}" radius="0.005" x="0.0801996" y="0.971876" z="1.19311"/> <node id="{a6567353-5b7a-4de0-8d0f-b916fd8a7287}" partId="{8d55eb65-52a8-47da-9b5b-7d6d914943cc}" radius="0.005" x="0.0801996" y="0.971876" z="1.19311"/>
<node id="{b078b7da-3fce-44fb-9dd0-9812f579991e}" partId="{019249f8-0f48-4f9e-87e3-6d2e821e87ac}" radius="0.005" x="0.0801996" y="0.971906" z="1.21121"/> <node id="{b078b7da-3fce-44fb-9dd0-9812f579991e}" partId="{65aa6ccd-94db-47cf-aa84-dce3c2b3fa65}" radius="0.005" x="0.0801996" y="0.971906" z="1.21121"/>
<node id="{b1a0502c-3215-461f-b003-4edd9a13562b}" partId="{983cf6ce-44d9-41e8-8489-289b423a423a}" radius="0.005" x="0.438446" y="0.55879" z="1.07822"/> <node id="{b1a0502c-3215-461f-b003-4edd9a13562b}" partId="{1f878838-6686-4bcc-bc82-d9c5583d05e8}" radius="0.005" x="0.438446" y="0.55879" z="1.07822"/>
<node boneMark="Limb" id="{b1aec088-f56d-4b96-b34b-4602276b67f4}" partId="{d2be1f6f-2053-491c-962b-f27e1a809848}" radius="0.0316832" x="0.428503" y="0.310597" z="1.17886"/> <node boneMark="Limb" id="{b1aec088-f56d-4b96-b34b-4602276b67f4}" partId="{93d5b991-f821-4974-96bd-17c46090ffa9}" radius="0.0316832" x="0.428503" y="0.310597" z="1.17886"/>
<node id="{b5c30ec3-4045-4502-911b-bc29b8a93496}" partId="{d4c0d323-791a-4a2a-ad1b-8e8fb93bf42d}" radius="0.0079208" x="0.0861396" y="0.956445" z="1.19721"/> <node id="{b5c30ec3-4045-4502-911b-bc29b8a93496}" partId="{8d55eb65-52a8-47da-9b5b-7d6d914943cc}" radius="0.0079208" x="0.0861396" y="0.956445" z="1.19721"/>
<node id="{b5f2d7c5-24dd-41e3-9dce-47e8db05ca86}" partId="{ad2259a2-362c-499e-9123-d6f8ac0a1966}" radius="0.110891" x="0.29505" y="0.59604" z="1.2198"/> <node id="{b5f2d7c5-24dd-41e3-9dce-47e8db05ca86}" partId="{422ad92f-8ad5-402a-9629-2348eb14e103}" radius="0.110891" x="0.29505" y="0.59604" z="1.2198"/>
<node id="{b7f18151-9b31-4e34-ad41-3d7a0358b1cd}" partId="{15325291-8856-42d7-9bc0-eb9ea53eadc1}" radius="0.00594061" x="0.447197" y="0.950065" z="1.2098"/> <node id="{b7f18151-9b31-4e34-ad41-3d7a0358b1cd}" partId="{3d3ba42b-7be6-4174-96c3-500daab0dc7f}" radius="0.00594061" x="0.447197" y="0.950065" z="1.2098"/>
<node boneMark="Joint" id="{bb1434c7-51ad-4226-a6e0-2e0ee5c626d7}" partId="{d5ccb7d6-3357-473c-a26a-12ce5713d92e}" radius="0.019802" x="0.154596" y="0.464592" z="1.11268"/> <node boneMark="Joint" id="{bb1434c7-51ad-4226-a6e0-2e0ee5c626d7}" partId="{9c9383da-b1ad-4fed-ac7d-e65d9334882f}" radius="0.019802" x="0.154596" y="0.464592" z="1.11268"/>
<node id="{bcb62c53-e5db-48ac-bbac-38159a42dfe3}" partId="{57622c57-5929-4a6a-b4ac-17dc909f002a}" radius="0.019802" x="0.293069" y="0.875248" z="1.42178"/> <node id="{bcb62c53-e5db-48ac-bbac-38159a42dfe3}" partId="{0a57548e-12d8-4488-9ce4-8fe3b64db253}" radius="0.019802" x="0.293069" y="0.875248" z="1.42178"/>
<node id="{bfb08a33-feee-4741-9855-64add2915ec4}" partId="{546efa0c-392d-47c5-80c1-4138b07cf0a4}" radius="0.005" x="0.171813" y="0.521107" z="1.09604"/> <node id="{bfb08a33-feee-4741-9855-64add2915ec4}" partId="{2162fa67-7e23-4e5f-9a9d-4175b10c44c9}" radius="0.005" x="0.171813" y="0.521107" z="1.09604"/>
<node id="{c03d7db7-dcba-4926-b22d-04bd37ed3b65}" partId="{6c3c3e49-1701-4f8c-9490-32d51f52fe9f}" radius="0.00792081" x="0.447197" y="0.950644" z="1.20846"/> <node id="{c03d7db7-dcba-4926-b22d-04bd37ed3b65}" partId="{b049bb5c-a4e5-402e-88db-a5cccbee3851}" radius="0.00792081" x="0.447197" y="0.950644" z="1.20846"/>
<node id="{c0535c22-dc84-4a77-84fc-d04b2d839575}" partId="{019249f8-0f48-4f9e-87e3-6d2e821e87ac}" radius="0.0079208" x="0.0861396" y="0.955949" z="1.21069"/> <node id="{c0535c22-dc84-4a77-84fc-d04b2d839575}" partId="{65aa6ccd-94db-47cf-aa84-dce3c2b3fa65}" radius="0.0079208" x="0.0861396" y="0.955949" z="1.21069"/>
<node id="{c0cb6b06-3876-4550-9c05-29110ae1f2d9}" partId="{6c3c3e49-1701-4f8c-9490-32d51f52fe9f}" radius="0.005" x="0.474917" y="0.983137" z="1.2311"/> <node id="{c0cb6b06-3876-4550-9c05-29110ae1f2d9}" partId="{b049bb5c-a4e5-402e-88db-a5cccbee3851}" radius="0.005" x="0.474917" y="0.983137" z="1.2311"/>
<node id="{c578f2fb-079d-43a6-873d-6e094e7df09a}" partId="{57622c57-5929-4a6a-b4ac-17dc909f002a}" radius="0.00792079" x="0.293069" y="0.845545" z="1.48713"/> <node id="{c578f2fb-079d-43a6-873d-6e094e7df09a}" partId="{0a57548e-12d8-4488-9ce4-8fe3b64db253}" radius="0.00792079" x="0.293069" y="0.845545" z="1.48713"/>
<node id="{ca7d354f-4221-4301-ba44-4ee63ac652de}" partId="{546efa0c-392d-47c5-80c1-4138b07cf0a4}" radius="0.0118812" x="0.158689" y="0.468864" z="1.10792"/> <node id="{ca7d354f-4221-4301-ba44-4ee63ac652de}" partId="{2162fa67-7e23-4e5f-9a9d-4175b10c44c9}" radius="0.0118812" x="0.158689" y="0.468864" z="1.10792"/>
<node id="{cb6c4607-f520-4a2e-b282-e54e2cb8165a}" partId="{fe5e25e8-58cb-43d5-ba72-c64e2cf44d6d}" radius="0.0673267" x="0.293069" y="0.0772277" z="1.17228"/> <node id="{cb6c4607-f520-4a2e-b282-e54e2cb8165a}" partId="{6ff1a4e8-b864-4c5e-8a8e-2d28edd2c55a}" radius="0.0673267" x="0.293069" y="0.0772277" z="1.17228"/>
<node id="{d00956a4-1164-40c6-b6f8-b601e504b7fd}" partId="{2c899cf6-d358-454e-ad79-5a5bddaf0bff}" radius="0.0178218" x="0.330693" y="0.0495049" z="1.10792"/> <node id="{d00956a4-1164-40c6-b6f8-b601e504b7fd}" partId="{3145e23e-d0bf-4f3f-8609-92f40c348fa5}" radius="0.0178218" x="0.330693" y="0.0495049" z="1.10792"/>
<node id="{d0250089-5e1a-43f7-b5f8-363642c7ae79}" partId="{881a0adc-3b0d-4575-b7d7-d6df8907e3f3}" radius="0.019802" x="0.139614" y="0.922043" z="1.22377"/> <node id="{d0250089-5e1a-43f7-b5f8-363642c7ae79}" partId="{79289522-791a-488f-bd2d-15db6c8f2f84}" radius="0.019802" x="0.139614" y="0.922043" z="1.22377"/>
<node id="{d2f082e0-13c8-4bc5-8c05-06cea9d46463}" partId="{fe5e25e8-58cb-43d5-ba72-c64e2cf44d6d}" radius="0.0514851" x="0.293069" y="0.0594059" z="1.10693"/> <node id="{d2f082e0-13c8-4bc5-8c05-06cea9d46463}" partId="{6ff1a4e8-b864-4c5e-8a8e-2d28edd2c55a}" radius="0.0514851" x="0.293069" y="0.0594059" z="1.10693"/>
<node id="{d7dd0c55-e568-413d-99f0-cf91bcc4c534}" partId="{2dacf558-7216-4577-8631-16769f860e18}" radius="0.0118812" x="0.149328" y="0.496134" z="1.0901"/> <node id="{d7dd0c55-e568-413d-99f0-cf91bcc4c534}" partId="{828c4638-7c11-4aeb-8f23-773dbeea8440}" radius="0.0118812" x="0.149328" y="0.496134" z="1.0901"/>
<node boneMark="Joint" id="{da8b7964-8659-41c2-aeee-aa3a1bf7d39c}" partId="{d2be1f6f-2053-491c-962b-f27e1a809848}" radius="0.019802" x="0.454264" y="0.481995" z="1.08619"/> <node boneMark="Joint" id="{da8b7964-8659-41c2-aeee-aa3a1bf7d39c}" partId="{93d5b991-f821-4974-96bd-17c46090ffa9}" radius="0.019802" x="0.454264" y="0.481995" z="1.08619"/>
<node id="{df5207c8-afdb-4305-b322-33de580b7379}" partId="{ad2259a2-362c-499e-9123-d6f8ac0a1966}" radius="0.0990099" x="0.29505" y="0.69901" z="1.22772"/> <node id="{df5207c8-afdb-4305-b322-33de580b7379}" partId="{422ad92f-8ad5-402a-9629-2348eb14e103}" radius="0.0990099" x="0.29505" y="0.69901" z="1.22772"/>
<node id="{e73bbdbf-3ba8-4a70-99e0-bde3f7f1e5cd}" partId="{546efa0c-392d-47c5-80c1-4138b07cf0a4}" radius="0.0118812" x="0.169102" y="0.49717" z="1.0901"/> <node id="{e73bbdbf-3ba8-4a70-99e0-bde3f7f1e5cd}" partId="{2162fa67-7e23-4e5f-9a9d-4175b10c44c9}" radius="0.0118812" x="0.169102" y="0.49717" z="1.0901"/>
<node id="{e767a69a-e29d-4fbc-8e1d-615a443f40f5}" partId="{2c899cf6-d358-454e-ad79-5a5bddaf0bff}" radius="0.0237624" x="0.348515" y="0.0475248" z="1.13366"/> <node id="{e767a69a-e29d-4fbc-8e1d-615a443f40f5}" partId="{3145e23e-d0bf-4f3f-8609-92f40c348fa5}" radius="0.0237624" x="0.348515" y="0.0475248" z="1.13366"/>
<node id="{ef8ef04c-4d5b-4663-a607-d1fa25995a0e}" partId="{d2be1f6f-2053-491c-962b-f27e1a809848}" radius="0.039604" x="0.327654" y="0.256375" z="1.18956"/> <node id="{ef8ef04c-4d5b-4663-a607-d1fa25995a0e}" partId="{93d5b991-f821-4974-96bd-17c46090ffa9}" radius="0.039604" x="0.327654" y="0.256375" z="1.18956"/>
<node id="{f06c29c3-080f-48d0-8ed5-e6fd3fabd51d}" partId="{fe5e25e8-58cb-43d5-ba72-c64e2cf44d6d}" radius="0.0237624" x="0.293069" y="0.0653465" z="1.05149"/> <node id="{f06c29c3-080f-48d0-8ed5-e6fd3fabd51d}" partId="{6ff1a4e8-b864-4c5e-8a8e-2d28edd2c55a}" radius="0.0237624" x="0.293069" y="0.0653465" z="1.05149"/>
<node boneMark="Joint" id="{f28a9edd-92c5-4e9e-9b69-e63ec9fd7f4f}" partId="{57622c57-5929-4a6a-b4ac-17dc909f002a}" radius="0.0158416" x="0.293069" y="0.889109" z="1.39406"/> <node boneMark="Joint" id="{f28a9edd-92c5-4e9e-9b69-e63ec9fd7f4f}" partId="{0a57548e-12d8-4488-9ce4-8fe3b64db253}" radius="0.0158416" x="0.293069" y="0.889109" z="1.39406"/>
<node id="{f467cb17-e94e-43f1-822c-c95dcb59c4a1}" partId="{15325291-8856-42d7-9bc0-eb9ea53eadc1}" radius="0.005" x="0.474917" y="0.981808" z="1.21121"/> <node id="{f467cb17-e94e-43f1-822c-c95dcb59c4a1}" partId="{3d3ba42b-7be6-4174-96c3-500daab0dc7f}" radius="0.005" x="0.474917" y="0.981808" z="1.21121"/>
<node id="{f53d85ae-0a06-47aa-8402-b5e0124cff66}" partId="{881a0adc-3b0d-4575-b7d7-d6df8907e3f3}" radius="0.0316832" x="0.145682" y="0.830619" z="1.22179"/> <node id="{f53d85ae-0a06-47aa-8402-b5e0124cff66}" partId="{79289522-791a-488f-bd2d-15db6c8f2f84}" radius="0.0316832" x="0.145682" y="0.830619" z="1.22179"/>
<node id="{f700b2e2-8fa0-4ea7-87e7-9a668edc7749}" partId="{442a221d-5a04-4d09-9cfd-bdd138320c26}" radius="0.0079208" x="0.10792" y="0.94213" z="1.2028"/> <node id="{f700b2e2-8fa0-4ea7-87e7-9a668edc7749}" partId="{de6790b2-a8be-42ea-9f96-75c8ac21c6da}" radius="0.0079208" x="0.10792" y="0.94213" z="1.2028"/>
<node id="{f89eea85-45e0-47d7-a009-96e2086d58fc}" partId="{57622c57-5929-4a6a-b4ac-17dc909f002a}" radius="0.0475248" x="0.293069" y="0.746535" z="1.2604"/> <node id="{f89eea85-45e0-47d7-a009-96e2086d58fc}" partId="{0a57548e-12d8-4488-9ce4-8fe3b64db253}" radius="0.0475248" x="0.293069" y="0.746535" z="1.2604"/>
<node id="{f94eabec-5f95-49f4-98ef-dd639838aaf0}" partId="{0b8f8da9-d461-48c4-bbfd-343f0b648235}" radius="0.005" x="0.460301" y="0.559628" z="1.07822"/> <node id="{f94eabec-5f95-49f4-98ef-dd639838aaf0}" partId="{0048f320-55a9-4777-914e-95944cb34b46}" radius="0.005" x="0.460301" y="0.559628" z="1.07822"/>
<node id="{fa3fc46e-827d-47b3-b266-5ad5728d9247}" partId="{2dacf558-7216-4577-8631-16769f860e18}" radius="0.0118812" x="0.150778" y="0.468449" z="1.10792"/> <node id="{fa3fc46e-827d-47b3-b266-5ad5728d9247}" partId="{828c4638-7c11-4aeb-8f23-773dbeea8440}" radius="0.0118812" x="0.150778" y="0.468449" z="1.10792"/>
<node id="{fa9f59c1-5451-4e3f-839a-39ae13615edf}" partId="{15325291-8856-42d7-9bc0-eb9ea53eadc1}" radius="0.0079208" x="0.468977" y="0.965851" z="1.21069"/> <node id="{fa9f59c1-5451-4e3f-839a-39ae13615edf}" partId="{3d3ba42b-7be6-4174-96c3-500daab0dc7f}" radius="0.0079208" x="0.468977" y="0.965851" z="1.21069"/>
<node id="{feacc347-69c3-4789-b565-9ac8156fca41}" partId="{b27111c8-625f-405d-88a9-55df58d51de0}" radius="0.039604" x="0.343555" y="0.567979" z="1.20099"/> <node id="{feacc347-69c3-4789-b565-9ac8156fca41}" partId="{5f080e1f-48f5-4aa7-aa0c-d175f9045341}" radius="0.039604" x="0.343555" y="0.567979" z="1.20099"/>
</nodes> </nodes>
<edges> <edges>
<edge from="{b1aec088-f56d-4b96-b34b-4602276b67f4}" id="{03559a5e-eca9-4b9b-a085-1badd686158a}" partId="{d2be1f6f-2053-491c-962b-f27e1a809848}" to="{8c4b6bea-3045-4b62-ba65-442c7c5548b9}"/> <edge from="{b1aec088-f56d-4b96-b34b-4602276b67f4}" id="{03559a5e-eca9-4b9b-a085-1badd686158a}" partId="{93d5b991-f821-4974-96bd-17c46090ffa9}" to="{8c4b6bea-3045-4b62-ba65-442c7c5548b9}"/>
<edge from="{d00956a4-1164-40c6-b6f8-b601e504b7fd}" id="{0bc68793-fa92-4765-bf48-8479bc222295}" partId="{2c899cf6-d358-454e-ad79-5a5bddaf0bff}" to="{e767a69a-e29d-4fbc-8e1d-615a443f40f5}"/> <edge from="{d00956a4-1164-40c6-b6f8-b601e504b7fd}" id="{0bc68793-fa92-4765-bf48-8479bc222295}" partId="{3145e23e-d0bf-4f3f-8609-92f40c348fa5}" to="{e767a69a-e29d-4fbc-8e1d-615a443f40f5}"/>
<edge from="{e73bbdbf-3ba8-4a70-99e0-bde3f7f1e5cd}" id="{0c191a67-6941-4607-bbb6-2cfabf551aac}" partId="{546efa0c-392d-47c5-80c1-4138b07cf0a4}" to="{bfb08a33-feee-4741-9855-64add2915ec4}"/> <edge from="{e73bbdbf-3ba8-4a70-99e0-bde3f7f1e5cd}" id="{0c191a67-6941-4607-bbb6-2cfabf551aac}" partId="{2162fa67-7e23-4e5f-9a9d-4175b10c44c9}" to="{bfb08a33-feee-4741-9855-64add2915ec4}"/>
<edge from="{0369aab4-b9b7-4d53-bc43-118f226dadc9}" id="{0c59cfdd-95a6-46f4-ab18-e088ed966657}" partId="{019249f8-0f48-4f9e-87e3-6d2e821e87ac}" to="{c0535c22-dc84-4a77-84fc-d04b2d839575}"/> <edge from="{0369aab4-b9b7-4d53-bc43-118f226dadc9}" id="{0c59cfdd-95a6-46f4-ab18-e088ed966657}" partId="{65aa6ccd-94db-47cf-aa84-dce3c2b3fa65}" to="{c0535c22-dc84-4a77-84fc-d04b2d839575}"/>
<edge from="{8f14d451-9613-48ef-8670-124223236475}" id="{0fb2cf45-efab-48dd-baa6-1ae968ac7dca}" partId="{91850869-d0e1-4e42-a452-d1259dbd5643}" to="{9bc24536-52b6-42c4-801e-6994069ae93b}"/> <edge from="{8f14d451-9613-48ef-8670-124223236475}" id="{0fb2cf45-efab-48dd-baa6-1ae968ac7dca}" partId="{c2b415b2-4419-408e-be54-1b6b2521441a}" to="{9bc24536-52b6-42c4-801e-6994069ae93b}"/>
<edge from="{a4935618-a165-4f84-aabf-c6d317c7a714}" id="{1df5cc4c-288a-4d34-bd8e-1f0eb01cba30}" partId="{0b8f8da9-d461-48c4-bbfd-343f0b648235}" to="{f94eabec-5f95-49f4-98ef-dd639838aaf0}"/> <edge from="{a4935618-a165-4f84-aabf-c6d317c7a714}" id="{1df5cc4c-288a-4d34-bd8e-1f0eb01cba30}" partId="{0048f320-55a9-4777-914e-95944cb34b46}" to="{f94eabec-5f95-49f4-98ef-dd639838aaf0}"/>
<edge from="{b7f18151-9b31-4e34-ad41-3d7a0358b1cd}" id="{1fdac3a3-9ff7-4375-aa4c-aee852e9cba6}" partId="{15325291-8856-42d7-9bc0-eb9ea53eadc1}" to="{fa9f59c1-5451-4e3f-839a-39ae13615edf}"/> <edge from="{b7f18151-9b31-4e34-ad41-3d7a0358b1cd}" id="{1fdac3a3-9ff7-4375-aa4c-aee852e9cba6}" partId="{3d3ba42b-7be6-4174-96c3-500daab0dc7f}" to="{fa9f59c1-5451-4e3f-839a-39ae13615edf}"/>
<edge from="{653c799b-da4a-4d0a-9be5-84bbe3ae7551}" id="{23fea5bb-2593-477e-8518-756ac0ab113f}" partId="{0b8f8da9-d461-48c4-bbfd-343f0b648235}" to="{0cecd379-b9d6-4568-9d5b-82720916a6e4}"/> <edge from="{653c799b-da4a-4d0a-9be5-84bbe3ae7551}" id="{23fea5bb-2593-477e-8518-756ac0ab113f}" partId="{0048f320-55a9-4777-914e-95944cb34b46}" to="{0cecd379-b9d6-4568-9d5b-82720916a6e4}"/>
<edge from="{f28a9edd-92c5-4e9e-9b69-e63ec9fd7f4f}" id="{2662a435-be76-40be-84b0-1632ffcf151a}" partId="{57622c57-5929-4a6a-b4ac-17dc909f002a}" to="{bcb62c53-e5db-48ac-bbac-38159a42dfe3}"/> <edge from="{f28a9edd-92c5-4e9e-9b69-e63ec9fd7f4f}" id="{2662a435-be76-40be-84b0-1632ffcf151a}" partId="{0a57548e-12d8-4488-9ce4-8fe3b64db253}" to="{bcb62c53-e5db-48ac-bbac-38159a42dfe3}"/>
<edge from="{d0250089-5e1a-43f7-b5f8-363642c7ae79}" id="{27cad29d-2627-4b05-84d2-2750c54e60b5}" partId="{881a0adc-3b0d-4575-b7d7-d6df8907e3f3}" to="{4c1029e5-c16d-442c-bc48-bdeed5d4706a}"/> <edge from="{d0250089-5e1a-43f7-b5f8-363642c7ae79}" id="{27cad29d-2627-4b05-84d2-2750c54e60b5}" partId="{79289522-791a-488f-bd2d-15db6c8f2f84}" to="{4c1029e5-c16d-442c-bc48-bdeed5d4706a}"/>
<edge from="{5ff7a560-b046-4ddd-b26e-7dfbcc1083b3}" id="{29ef255d-fb26-46af-8acb-0731e1e182f3}" partId="{983cf6ce-44d9-41e8-8489-289b423a423a}" to="{83e2e87a-2fd0-4df4-8278-28dd30482f67}"/> <edge from="{5ff7a560-b046-4ddd-b26e-7dfbcc1083b3}" id="{29ef255d-fb26-46af-8acb-0731e1e182f3}" partId="{1f878838-6686-4bcc-bc82-d9c5583d05e8}" to="{83e2e87a-2fd0-4df4-8278-28dd30482f67}"/>
<edge from="{9d2b664d-a090-4c9c-b8a3-6a07d8d0eb08}" id="{339d89cb-a895-456a-8efe-9c80c47fa0fc}" partId="{fe5e25e8-58cb-43d5-ba72-c64e2cf44d6d}" to="{f06c29c3-080f-48d0-8ed5-e6fd3fabd51d}"/> <edge from="{9d2b664d-a090-4c9c-b8a3-6a07d8d0eb08}" id="{339d89cb-a895-456a-8efe-9c80c47fa0fc}" partId="{6ff1a4e8-b864-4c5e-8a8e-2d28edd2c55a}" to="{f06c29c3-080f-48d0-8ed5-e6fd3fabd51d}"/>
<edge from="{d7dd0c55-e568-413d-99f0-cf91bcc4c534}" id="{39358088-3f7b-400e-886b-01576860291d}" partId="{2dacf558-7216-4577-8631-16769f860e18}" to="{3c4fd98e-c829-43f2-9ae5-c3c1c064a0a8}"/> <edge from="{d7dd0c55-e568-413d-99f0-cf91bcc4c534}" id="{39358088-3f7b-400e-886b-01576860291d}" partId="{828c4638-7c11-4aeb-8f23-773dbeea8440}" to="{3c4fd98e-c829-43f2-9ae5-c3c1c064a0a8}"/>
<edge from="{98e623f4-0c83-4da8-9ba8-c4d470f0c5f0}" id="{3e3290b7-49b3-47a2-9bed-325b13b8f13f}" partId="{6c3c3e49-1701-4f8c-9490-32d51f52fe9f}" to="{c0cb6b06-3876-4550-9c05-29110ae1f2d9}"/> <edge from="{98e623f4-0c83-4da8-9ba8-c4d470f0c5f0}" id="{3e3290b7-49b3-47a2-9bed-325b13b8f13f}" partId="{b049bb5c-a4e5-402e-88db-a5cccbee3851}" to="{c0cb6b06-3876-4550-9c05-29110ae1f2d9}"/>
<edge from="{cb6c4607-f520-4a2e-b282-e54e2cb8165a}" id="{457abb0d-0c84-4d74-9e94-bf366c3c341b}" partId="{fe5e25e8-58cb-43d5-ba72-c64e2cf44d6d}" to="{78d00d69-b1b9-4fad-b0cc-87f9b6f1539f}"/> <edge from="{cb6c4607-f520-4a2e-b282-e54e2cb8165a}" id="{457abb0d-0c84-4d74-9e94-bf366c3c341b}" partId="{6ff1a4e8-b864-4c5e-8a8e-2d28edd2c55a}" to="{78d00d69-b1b9-4fad-b0cc-87f9b6f1539f}"/>
<edge from="{0cecd379-b9d6-4568-9d5b-82720916a6e4}" id="{47a88fde-5580-411f-afef-edb3742e6b5d}" partId="{0b8f8da9-d461-48c4-bbfd-343f0b648235}" to="{a4935618-a165-4f84-aabf-c6d317c7a714}"/> <edge from="{0cecd379-b9d6-4568-9d5b-82720916a6e4}" id="{47a88fde-5580-411f-afef-edb3742e6b5d}" partId="{0048f320-55a9-4777-914e-95944cb34b46}" to="{a4935618-a165-4f84-aabf-c6d317c7a714}"/>
<edge from="{4946f825-7960-4653-9148-8d21e269c74e}" id="{4ee5c0b3-ac1e-4918-92ea-957b4f996fcc}" partId="{d5ccb7d6-3357-473c-a26a-12ce5713d92e}" to="{50c1e61f-d968-4d2f-b4c2-9839326f49da}"/> <edge from="{4946f825-7960-4653-9148-8d21e269c74e}" id="{4ee5c0b3-ac1e-4918-92ea-957b4f996fcc}" partId="{9c9383da-b1ad-4fed-ac7d-e65d9334882f}" to="{50c1e61f-d968-4d2f-b4c2-9839326f49da}"/>
<edge from="{3e59449e-b487-4708-8b3e-31242cb50d19}" id="{5480ad4e-1ec3-496c-8841-517dee20eff1}" partId="{881a0adc-3b0d-4575-b7d7-d6df8907e3f3}" to="{13bc43e1-b35e-4645-8fd5-e9e8b16d5fc8}"/> <edge from="{3e59449e-b487-4708-8b3e-31242cb50d19}" id="{5480ad4e-1ec3-496c-8841-517dee20eff1}" partId="{79289522-791a-488f-bd2d-15db6c8f2f84}" to="{13bc43e1-b35e-4645-8fd5-e9e8b16d5fc8}"/>
<edge from="{8642cce7-c158-4559-99af-bf2caa121348}" id="{57eeedc3-4844-42f5-90d3-176d8b9a0ded}" partId="{d2be1f6f-2053-491c-962b-f27e1a809848}" to="{69d7c60a-eaa7-4d64-a09b-755fb3b4c9ae}"/> <edge from="{8642cce7-c158-4559-99af-bf2caa121348}" id="{57eeedc3-4844-42f5-90d3-176d8b9a0ded}" partId="{93d5b991-f821-4974-96bd-17c46090ffa9}" to="{69d7c60a-eaa7-4d64-a09b-755fb3b4c9ae}"/>
<edge from="{69d7c60a-eaa7-4d64-a09b-755fb3b4c9ae}" id="{5bdecde8-59e6-4c52-bf16-775df40f8207}" partId="{d2be1f6f-2053-491c-962b-f27e1a809848}" to="{da8b7964-8659-41c2-aeee-aa3a1bf7d39c}"/> <edge from="{69d7c60a-eaa7-4d64-a09b-755fb3b4c9ae}" id="{5bdecde8-59e6-4c52-bf16-775df40f8207}" partId="{93d5b991-f821-4974-96bd-17c46090ffa9}" to="{da8b7964-8659-41c2-aeee-aa3a1bf7d39c}"/>
<edge from="{27721191-b625-44bd-9253-cfc7df7b094b}" id="{5de6c308-5fa9-48a8-b3c4-173fbeb3c4d7}" partId="{dd604661-6acc-4ef1-83fc-36a374c54734}" to="{05872b14-1e9a-4ba9-bbf8-0111c9100527}"/> <edge from="{27721191-b625-44bd-9253-cfc7df7b094b}" id="{5de6c308-5fa9-48a8-b3c4-173fbeb3c4d7}" partId="{ff29229a-564e-4e50-af37-0ae6c25b78ae}" to="{05872b14-1e9a-4ba9-bbf8-0111c9100527}"/>
<edge from="{a019b2a5-f245-4139-b1a2-6b9462326c51}" id="{604937ca-7961-439f-87a0-2979b2bba504}" partId="{983cf6ce-44d9-41e8-8489-289b423a423a}" to="{5ff7a560-b046-4ddd-b26e-7dfbcc1083b3}"/> <edge from="{a019b2a5-f245-4139-b1a2-6b9462326c51}" id="{604937ca-7961-439f-87a0-2979b2bba504}" partId="{1f878838-6686-4bcc-bc82-d9c5583d05e8}" to="{5ff7a560-b046-4ddd-b26e-7dfbcc1083b3}"/>
<edge from="{6954e176-c739-40d8-b1ef-8ce38884bba2}" id="{67791486-597e-407f-b7fa-fcdd952976d5}" partId="{881a0adc-3b0d-4575-b7d7-d6df8907e3f3}" to="{d0250089-5e1a-43f7-b5f8-363642c7ae79}"/> <edge from="{6954e176-c739-40d8-b1ef-8ce38884bba2}" id="{67791486-597e-407f-b7fa-fcdd952976d5}" partId="{79289522-791a-488f-bd2d-15db6c8f2f84}" to="{d0250089-5e1a-43f7-b5f8-363642c7ae79}"/>
<edge from="{79b757ee-5f39-4a71-8019-5e447daa9860}" id="{691fca9b-c174-480d-910a-084130308108}" partId="{d4c0d323-791a-4a2a-ad1b-8e8fb93bf42d}" to="{b5c30ec3-4045-4502-911b-bc29b8a93496}"/> <edge from="{79b757ee-5f39-4a71-8019-5e447daa9860}" id="{691fca9b-c174-480d-910a-084130308108}" partId="{8d55eb65-52a8-47da-9b5b-7d6d914943cc}" to="{b5c30ec3-4045-4502-911b-bc29b8a93496}"/>
<edge from="{5d85b12a-fdd1-42c7-83d4-027d77716fc8}" id="{69cf3563-054e-4b4e-88b3-9077aba0309c}" partId="{c9c2c2b9-571b-4067-8b01-b59725670dd4}" to="{273c09b7-d466-496f-9d06-ef7fa267c9a7}"/> <edge from="{5d85b12a-fdd1-42c7-83d4-027d77716fc8}" id="{69cf3563-054e-4b4e-88b3-9077aba0309c}" partId="{c569a92e-6b1b-4d8b-9284-76fef9c124f0}" to="{273c09b7-d466-496f-9d06-ef7fa267c9a7}"/>
<edge from="{78f63ac5-8fc8-40dd-80e4-b316b3b4b409}" id="{69dd2419-2eae-4dad-9e0b-857099719a24}" partId="{dd604661-6acc-4ef1-83fc-36a374c54734}" to="{27721191-b625-44bd-9253-cfc7df7b094b}"/> <edge from="{78f63ac5-8fc8-40dd-80e4-b316b3b4b409}" id="{69dd2419-2eae-4dad-9e0b-857099719a24}" partId="{ff29229a-564e-4e50-af37-0ae6c25b78ae}" to="{27721191-b625-44bd-9253-cfc7df7b094b}"/>
<edge from="{ca7d354f-4221-4301-ba44-4ee63ac652de}" id="{71893b64-d141-4d64-a360-066f34d22cfe}" partId="{546efa0c-392d-47c5-80c1-4138b07cf0a4}" to="{e73bbdbf-3ba8-4a70-99e0-bde3f7f1e5cd}"/> <edge from="{ca7d354f-4221-4301-ba44-4ee63ac652de}" id="{71893b64-d141-4d64-a360-066f34d22cfe}" partId="{2162fa67-7e23-4e5f-9a9d-4175b10c44c9}" to="{e73bbdbf-3ba8-4a70-99e0-bde3f7f1e5cd}"/>
<edge from="{fa9f59c1-5451-4e3f-839a-39ae13615edf}" id="{727a73c2-d792-449a-ad3d-2a2ea5807f3d}" partId="{15325291-8856-42d7-9bc0-eb9ea53eadc1}" to="{f467cb17-e94e-43f1-822c-c95dcb59c4a1}"/> <edge from="{fa9f59c1-5451-4e3f-839a-39ae13615edf}" id="{727a73c2-d792-449a-ad3d-2a2ea5807f3d}" partId="{3d3ba42b-7be6-4174-96c3-500daab0dc7f}" to="{f467cb17-e94e-43f1-822c-c95dcb59c4a1}"/>
<edge from="{b5f2d7c5-24dd-41e3-9dce-47e8db05ca86}" id="{781b9387-2b6e-4aa2-b944-69b41906b89e}" partId="{ad2259a2-362c-499e-9123-d6f8ac0a1966}" to="{df5207c8-afdb-4305-b322-33de580b7379}"/> <edge from="{b5f2d7c5-24dd-41e3-9dce-47e8db05ca86}" id="{781b9387-2b6e-4aa2-b944-69b41906b89e}" partId="{422ad92f-8ad5-402a-9629-2348eb14e103}" to="{df5207c8-afdb-4305-b322-33de580b7379}"/>
<edge from="{fa3fc46e-827d-47b3-b266-5ad5728d9247}" id="{783f01bb-a97c-4601-b1c0-5f6e17aa4e3a}" partId="{2dacf558-7216-4577-8631-16769f860e18}" to="{d7dd0c55-e568-413d-99f0-cf91bcc4c534}"/> <edge from="{fa3fc46e-827d-47b3-b266-5ad5728d9247}" id="{783f01bb-a97c-4601-b1c0-5f6e17aa4e3a}" partId="{828c4638-7c11-4aeb-8f23-773dbeea8440}" to="{d7dd0c55-e568-413d-99f0-cf91bcc4c534}"/>
<edge from="{6186ebb3-84a4-4f2c-9992-b39a3831dea3}" id="{7846da51-81d8-434f-b461-08c0e56aeb40}" partId="{57622c57-5929-4a6a-b4ac-17dc909f002a}" to="{457564d3-ccd0-465a-8d0b-b57c379666fd}"/> <edge from="{6186ebb3-84a4-4f2c-9992-b39a3831dea3}" id="{7846da51-81d8-434f-b461-08c0e56aeb40}" partId="{0a57548e-12d8-4488-9ce4-8fe3b64db253}" to="{457564d3-ccd0-465a-8d0b-b57c379666fd}"/>
<edge from="{13bc43e1-b35e-4645-8fd5-e9e8b16d5fc8}" id="{7cc566fa-0f48-421c-bfa8-fe802bcf7837}" partId="{881a0adc-3b0d-4575-b7d7-d6df8907e3f3}" to="{f53d85ae-0a06-47aa-8402-b5e0124cff66}"/> <edge from="{13bc43e1-b35e-4645-8fd5-e9e8b16d5fc8}" id="{7cc566fa-0f48-421c-bfa8-fe802bcf7837}" partId="{79289522-791a-488f-bd2d-15db6c8f2f84}" to="{f53d85ae-0a06-47aa-8402-b5e0124cff66}"/>
<edge from="{13d47f89-649c-4830-ba6d-fbf7f132793e}" id="{7cd31182-1d8a-4a5a-a860-8a57a92d780f}" partId="{9fae01c2-855a-472b-8181-02a1b76fcab1}" to="{5bad0bb7-9015-44cc-a57f-4200b20a84f0}"/> <edge from="{13d47f89-649c-4830-ba6d-fbf7f132793e}" id="{7cd31182-1d8a-4a5a-a860-8a57a92d780f}" partId="{b6e20100-8c2b-4550-aee8-806aa713c90c}" to="{5bad0bb7-9015-44cc-a57f-4200b20a84f0}"/>
<edge from="{feacc347-69c3-4789-b565-9ac8156fca41}" id="{7eab925b-db9e-4fe9-ae08-f953009c0cb2}" partId="{b27111c8-625f-405d-88a9-55df58d51de0}" to="{08f7ae7b-869a-4f40-87a4-a575d78c2a60}"/> <edge from="{feacc347-69c3-4789-b565-9ac8156fca41}" id="{7eab925b-db9e-4fe9-ae08-f953009c0cb2}" partId="{5f080e1f-48f5-4aa7-aa0c-d175f9045341}" to="{08f7ae7b-869a-4f40-87a4-a575d78c2a60}"/>
<edge from="{f700b2e2-8fa0-4ea7-87e7-9a668edc7749}" id="{80408bef-00bd-4a51-95a6-1960595f17ba}" partId="{442a221d-5a04-4d09-9cfd-bdd138320c26}" to="{5eb58123-c8a7-4899-a34e-1c63df33e759}"/> <edge from="{f700b2e2-8fa0-4ea7-87e7-9a668edc7749}" id="{80408bef-00bd-4a51-95a6-1960595f17ba}" partId="{de6790b2-a8be-42ea-9f96-75c8ac21c6da}" to="{5eb58123-c8a7-4899-a34e-1c63df33e759}"/>
<edge from="{944c7657-e978-4bb6-94ad-b03dbafae824}" id="{80e8f33c-7074-4a24-85c0-47c2508c6349}" partId="{d5ccb7d6-3357-473c-a26a-12ce5713d92e}" to="{bb1434c7-51ad-4226-a6e0-2e0ee5c626d7}"/> <edge from="{944c7657-e978-4bb6-94ad-b03dbafae824}" id="{80e8f33c-7074-4a24-85c0-47c2508c6349}" partId="{9c9383da-b1ad-4fed-ac7d-e65d9334882f}" to="{bb1434c7-51ad-4226-a6e0-2e0ee5c626d7}"/>
<edge from="{22f4fc91-c657-497f-aef8-87d38a42e23f}" id="{83113970-b08c-4f44-ad8f-f3ec8260fd73}" partId="{d5ccb7d6-3357-473c-a26a-12ce5713d92e}" to="{4946f825-7960-4653-9148-8d21e269c74e}"/> <edge from="{22f4fc91-c657-497f-aef8-87d38a42e23f}" id="{83113970-b08c-4f44-ad8f-f3ec8260fd73}" partId="{9c9383da-b1ad-4fed-ac7d-e65d9334882f}" to="{4946f825-7960-4653-9148-8d21e269c74e}"/>
<edge from="{83e2e87a-2fd0-4df4-8278-28dd30482f67}" id="{83308095-fe21-419b-8409-31ba40d9bb4f}" partId="{983cf6ce-44d9-41e8-8489-289b423a423a}" to="{b1a0502c-3215-461f-b003-4edd9a13562b}"/> <edge from="{83e2e87a-2fd0-4df4-8278-28dd30482f67}" id="{83308095-fe21-419b-8409-31ba40d9bb4f}" partId="{1f878838-6686-4bcc-bc82-d9c5583d05e8}" to="{b1a0502c-3215-461f-b003-4edd9a13562b}"/>
<edge from="{274f069c-1c6b-4837-8892-853ca652c07e}" id="{855fc650-6623-4dcf-a1cd-04fabd1ab5d3}" partId="{471dfce0-6e13-4146-b261-2090e30f407b}" to="{6e90e23a-c67b-4dc7-af76-2145793a9c3b}"/> <edge from="{274f069c-1c6b-4837-8892-853ca652c07e}" id="{855fc650-6623-4dcf-a1cd-04fabd1ab5d3}" partId="{aceff516-7672-4ab0-8de4-10fb8c7a0681}" to="{6e90e23a-c67b-4dc7-af76-2145793a9c3b}"/>
<edge from="{6b2d17cd-4fbe-4fd3-8f80-6c467d3811fd}" id="{884e4588-1fd9-4a4a-8ded-7c571f02606a}" partId="{471dfce0-6e13-4146-b261-2090e30f407b}" to="{274f069c-1c6b-4837-8892-853ca652c07e}"/> <edge from="{6b2d17cd-4fbe-4fd3-8f80-6c467d3811fd}" id="{884e4588-1fd9-4a4a-8ded-7c571f02606a}" partId="{aceff516-7672-4ab0-8de4-10fb8c7a0681}" to="{274f069c-1c6b-4837-8892-853ca652c07e}"/>
<edge from="{f06c29c3-080f-48d0-8ed5-e6fd3fabd51d}" id="{8906f231-ed91-47ce-8959-3ea190eb7381}" partId="{fe5e25e8-58cb-43d5-ba72-c64e2cf44d6d}" to="{d2f082e0-13c8-4bc5-8c05-06cea9d46463}"/> <edge from="{f06c29c3-080f-48d0-8ed5-e6fd3fabd51d}" id="{8906f231-ed91-47ce-8959-3ea190eb7381}" partId="{6ff1a4e8-b864-4c5e-8a8e-2d28edd2c55a}" to="{d2f082e0-13c8-4bc5-8c05-06cea9d46463}"/>
<edge from="{c0535c22-dc84-4a77-84fc-d04b2d839575}" id="{91257c7b-4fc7-4dbf-baf7-d42b475856d4}" partId="{019249f8-0f48-4f9e-87e3-6d2e821e87ac}" to="{b078b7da-3fce-44fb-9dd0-9812f579991e}"/> <edge from="{c0535c22-dc84-4a77-84fc-d04b2d839575}" id="{91257c7b-4fc7-4dbf-baf7-d42b475856d4}" partId="{65aa6ccd-94db-47cf-aa84-dce3c2b3fa65}" to="{b078b7da-3fce-44fb-9dd0-9812f579991e}"/>
<edge from="{9bc24536-52b6-42c4-801e-6994069ae93b}" id="{94792102-b5f4-4986-9b3f-7ceed3d3f78a}" partId="{91850869-d0e1-4e42-a452-d1259dbd5643}" to="{a43e38c8-aa79-4a6f-860b-65055bafcf62}"/> <edge from="{9bc24536-52b6-42c4-801e-6994069ae93b}" id="{94792102-b5f4-4986-9b3f-7ceed3d3f78a}" partId="{c2b415b2-4419-408e-be54-1b6b2521441a}" to="{a43e38c8-aa79-4a6f-860b-65055bafcf62}"/>
<edge from="{00da2bf5-53b1-495b-8f56-aaf78e74e9c2}" id="{97adbdb0-987f-46d3-815b-bb9c50f494d1}" partId="{57622c57-5929-4a6a-b4ac-17dc909f002a}" to="{c578f2fb-079d-43a6-873d-6e094e7df09a}"/> <edge from="{00da2bf5-53b1-495b-8f56-aaf78e74e9c2}" id="{97adbdb0-987f-46d3-815b-bb9c50f494d1}" partId="{0a57548e-12d8-4488-9ce4-8fe3b64db253}" to="{c578f2fb-079d-43a6-873d-6e094e7df09a}"/>
<edge from="{bfb08a33-feee-4741-9855-64add2915ec4}" id="{98e36f0b-f0b7-4d76-b0ac-284d0c945aaa}" partId="{546efa0c-392d-47c5-80c1-4138b07cf0a4}" to="{8b71e1a2-e0bc-435d-9bd2-c096f60c7f23}"/> <edge from="{bfb08a33-feee-4741-9855-64add2915ec4}" id="{98e36f0b-f0b7-4d76-b0ac-284d0c945aaa}" partId="{2162fa67-7e23-4e5f-9a9d-4175b10c44c9}" to="{8b71e1a2-e0bc-435d-9bd2-c096f60c7f23}"/>
<edge from="{50c1e61f-d968-4d2f-b4c2-9839326f49da}" id="{9973a3e5-fc86-485c-84ac-c9bdddcef463}" partId="{d5ccb7d6-3357-473c-a26a-12ce5713d92e}" to="{944c7657-e978-4bb6-94ad-b03dbafae824}"/> <edge from="{50c1e61f-d968-4d2f-b4c2-9839326f49da}" id="{9973a3e5-fc86-485c-84ac-c9bdddcef463}" partId="{9c9383da-b1ad-4fed-ac7d-e65d9334882f}" to="{944c7657-e978-4bb6-94ad-b03dbafae824}"/>
<edge from="{0891b311-79de-4ed7-8410-b3e43e675a9b}" id="{9baf5410-751e-44e2-a8dd-28f81d6b834b}" partId="{ad2259a2-362c-499e-9123-d6f8ac0a1966}" to="{7305f345-84d5-44b5-af37-c48bf3925ca4}"/> <edge from="{0891b311-79de-4ed7-8410-b3e43e675a9b}" id="{9baf5410-751e-44e2-a8dd-28f81d6b834b}" partId="{422ad92f-8ad5-402a-9629-2348eb14e103}" to="{7305f345-84d5-44b5-af37-c48bf3925ca4}"/>
<edge from="{c03d7db7-dcba-4926-b22d-04bd37ed3b65}" id="{aa2acb4a-8151-44e4-83d9-af6588273f03}" partId="{6c3c3e49-1701-4f8c-9490-32d51f52fe9f}" to="{98e623f4-0c83-4da8-9ba8-c4d470f0c5f0}"/> <edge from="{c03d7db7-dcba-4926-b22d-04bd37ed3b65}" id="{aa2acb4a-8151-44e4-83d9-af6588273f03}" partId="{b049bb5c-a4e5-402e-88db-a5cccbee3851}" to="{98e623f4-0c83-4da8-9ba8-c4d470f0c5f0}"/>
<edge from="{bcb62c53-e5db-48ac-bbac-38159a42dfe3}" id="{aae25104-da55-41df-a259-6124f8394c67}" partId="{57622c57-5929-4a6a-b4ac-17dc909f002a}" to="{00da2bf5-53b1-495b-8f56-aaf78e74e9c2}"/> <edge from="{bcb62c53-e5db-48ac-bbac-38159a42dfe3}" id="{aae25104-da55-41df-a259-6124f8394c67}" partId="{0a57548e-12d8-4488-9ce4-8fe3b64db253}" to="{00da2bf5-53b1-495b-8f56-aaf78e74e9c2}"/>
<edge from="{3d6b06a7-af07-48e4-849b-204cb9af6a69}" id="{b4182212-1b23-4672-b18f-99059a6e86e6}" partId="{881a0adc-3b0d-4575-b7d7-d6df8907e3f3}" to="{3e59449e-b487-4708-8b3e-31242cb50d19}"/> <edge from="{3d6b06a7-af07-48e4-849b-204cb9af6a69}" id="{b4182212-1b23-4672-b18f-99059a6e86e6}" partId="{79289522-791a-488f-bd2d-15db6c8f2f84}" to="{3e59449e-b487-4708-8b3e-31242cb50d19}"/>
<edge from="{25b52681-7ee3-458e-8db2-bf784e1c101d}" id="{b5b4b7f9-66bc-4312-b962-2e9b17431841}" partId="{b27111c8-625f-405d-88a9-55df58d51de0}" to="{9ad35b2b-3998-4822-9ecf-ff2425531106}"/> <edge from="{25b52681-7ee3-458e-8db2-bf784e1c101d}" id="{b5b4b7f9-66bc-4312-b962-2e9b17431841}" partId="{5f080e1f-48f5-4aa7-aa0c-d175f9045341}" to="{9ad35b2b-3998-4822-9ecf-ff2425531106}"/>
<edge from="{08f7ae7b-869a-4f40-87a4-a575d78c2a60}" id="{b5d52537-0126-473f-b846-e9776e763d6f}" partId="{b27111c8-625f-405d-88a9-55df58d51de0}" to="{2e54f33b-c879-4e40-83cc-75eeefaccdc3}"/> <edge from="{08f7ae7b-869a-4f40-87a4-a575d78c2a60}" id="{b5d52537-0126-473f-b846-e9776e763d6f}" partId="{5f080e1f-48f5-4aa7-aa0c-d175f9045341}" to="{2e54f33b-c879-4e40-83cc-75eeefaccdc3}"/>
<edge from="{9ad35b2b-3998-4822-9ecf-ff2425531106}" id="{b5fcb83f-4aee-4d3d-a105-e22272e7556f}" partId="{b27111c8-625f-405d-88a9-55df58d51de0}" to="{2073cdb1-77ef-4c51-8ca0-4ad4e0908d6c}"/> <edge from="{9ad35b2b-3998-4822-9ecf-ff2425531106}" id="{b5fcb83f-4aee-4d3d-a105-e22272e7556f}" partId="{5f080e1f-48f5-4aa7-aa0c-d175f9045341}" to="{2073cdb1-77ef-4c51-8ca0-4ad4e0908d6c}"/>
<edge from="{5eb58123-c8a7-4899-a34e-1c63df33e759}" id="{b84ac82e-48bb-4adb-bf52-d3fe98dc7fc3}" partId="{442a221d-5a04-4d09-9cfd-bdd138320c26}" to="{286523f2-bf39-4f2c-aae7-dcf7223140e4}"/> <edge from="{5eb58123-c8a7-4899-a34e-1c63df33e759}" id="{b84ac82e-48bb-4adb-bf52-d3fe98dc7fc3}" partId="{de6790b2-a8be-42ea-9f96-75c8ac21c6da}" to="{286523f2-bf39-4f2c-aae7-dcf7223140e4}"/>
<edge from="{273c09b7-d466-496f-9d06-ef7fa267c9a7}" id="{b8aa3f35-e0e1-4490-98c3-d3edcc4234d7}" partId="{c9c2c2b9-571b-4067-8b01-b59725670dd4}" to="{a003f447-592d-45db-ac31-50e1b0dacb02}"/> <edge from="{273c09b7-d466-496f-9d06-ef7fa267c9a7}" id="{b8aa3f35-e0e1-4490-98c3-d3edcc4234d7}" partId="{c569a92e-6b1b-4d8b-9284-76fef9c124f0}" to="{a003f447-592d-45db-ac31-50e1b0dacb02}"/>
<edge from="{2b5dae5c-17e3-4632-ab54-36e21859fbca}" id="{bbe18ec3-0537-4d8f-8cdb-3bff15339b18}" partId="{ad2259a2-362c-499e-9123-d6f8ac0a1966}" to="{0891b311-79de-4ed7-8410-b3e43e675a9b}"/> <edge from="{2b5dae5c-17e3-4632-ab54-36e21859fbca}" id="{bbe18ec3-0537-4d8f-8cdb-3bff15339b18}" partId="{422ad92f-8ad5-402a-9629-2348eb14e103}" to="{0891b311-79de-4ed7-8410-b3e43e675a9b}"/>
<edge from="{25e5be7a-dbb8-4d8f-a0b7-441eef6c694f}" id="{bdb3e1c9-df23-4482-9acb-2ef1aaf4da86}" partId="{d5ccb7d6-3357-473c-a26a-12ce5713d92e}" to="{22f4fc91-c657-497f-aef8-87d38a42e23f}"/> <edge from="{25e5be7a-dbb8-4d8f-a0b7-441eef6c694f}" id="{bdb3e1c9-df23-4482-9acb-2ef1aaf4da86}" partId="{9c9383da-b1ad-4fed-ac7d-e65d9334882f}" to="{22f4fc91-c657-497f-aef8-87d38a42e23f}"/>
<edge from="{7305f345-84d5-44b5-af37-c48bf3925ca4}" id="{be288181-dd71-413e-a459-fbc568ebeacf}" partId="{ad2259a2-362c-499e-9123-d6f8ac0a1966}" to="{b5f2d7c5-24dd-41e3-9dce-47e8db05ca86}"/> <edge from="{7305f345-84d5-44b5-af37-c48bf3925ca4}" id="{be288181-dd71-413e-a459-fbc568ebeacf}" partId="{422ad92f-8ad5-402a-9629-2348eb14e103}" to="{b5f2d7c5-24dd-41e3-9dce-47e8db05ca86}"/>
<edge from="{ef8ef04c-4d5b-4663-a607-d1fa25995a0e}" id="{c174d83a-8896-4fc8-8263-7d3d45624d2f}" partId="{d2be1f6f-2053-491c-962b-f27e1a809848}" to="{b1aec088-f56d-4b96-b34b-4602276b67f4}"/> <edge from="{ef8ef04c-4d5b-4663-a607-d1fa25995a0e}" id="{c174d83a-8896-4fc8-8263-7d3d45624d2f}" partId="{93d5b991-f821-4974-96bd-17c46090ffa9}" to="{b1aec088-f56d-4b96-b34b-4602276b67f4}"/>
<edge from="{b5c30ec3-4045-4502-911b-bc29b8a93496}" id="{c6dbe094-8524-4ddc-b4ee-50d4972c0d32}" partId="{d4c0d323-791a-4a2a-ad1b-8e8fb93bf42d}" to="{a6567353-5b7a-4de0-8d0f-b916fd8a7287}"/> <edge from="{b5c30ec3-4045-4502-911b-bc29b8a93496}" id="{c6dbe094-8524-4ddc-b4ee-50d4972c0d32}" partId="{8d55eb65-52a8-47da-9b5b-7d6d914943cc}" to="{a6567353-5b7a-4de0-8d0f-b916fd8a7287}"/>
<edge from="{78f63ac5-8fc8-40dd-80e4-b316b3b4b409}" id="{c9b8937c-b107-4b7d-bbca-ed9ff44511e6}" partId="{dd604661-6acc-4ef1-83fc-36a374c54734}" to="{4b8f93a6-db8f-47c8-8d37-777db444914c}"/> <edge from="{78f63ac5-8fc8-40dd-80e4-b316b3b4b409}" id="{c9b8937c-b107-4b7d-bbca-ed9ff44511e6}" partId="{ff29229a-564e-4e50-af37-0ae6c25b78ae}" to="{4b8f93a6-db8f-47c8-8d37-777db444914c}"/>
<edge from="{37ad0424-3633-48a9-a2ea-bac83d140ec8}" id="{cbc0c4f2-b380-4c9d-a558-ce0b8dc27df7}" partId="{b27111c8-625f-405d-88a9-55df58d51de0}" to="{25b52681-7ee3-458e-8db2-bf784e1c101d}"/> <edge from="{37ad0424-3633-48a9-a2ea-bac83d140ec8}" id="{cbc0c4f2-b380-4c9d-a558-ce0b8dc27df7}" partId="{5f080e1f-48f5-4aa7-aa0c-d175f9045341}" to="{25b52681-7ee3-458e-8db2-bf784e1c101d}"/>
<edge from="{3c4fd98e-c829-43f2-9ae5-c3c1c064a0a8}" id="{cc8fba76-684e-4dd1-bf89-4f7804bcd2b9}" partId="{2dacf558-7216-4577-8631-16769f860e18}" to="{5492a461-1b28-47c8-811c-2542022eff84}"/> <edge from="{3c4fd98e-c829-43f2-9ae5-c3c1c064a0a8}" id="{cc8fba76-684e-4dd1-bf89-4f7804bcd2b9}" partId="{828c4638-7c11-4aeb-8f23-773dbeea8440}" to="{5492a461-1b28-47c8-811c-2542022eff84}"/>
<edge from="{1e7e802c-3711-40c8-9dd6-bcded439cffe}" id="{cd25b802-82bf-4920-afa6-3f4e53b91467}" partId="{a5ad3f64-2091-4cea-8085-e17881f0573d}" to="{27fb4823-e857-4e61-a454-a12d30ecfb17}"/> <edge from="{1e7e802c-3711-40c8-9dd6-bcded439cffe}" id="{cd25b802-82bf-4920-afa6-3f4e53b91467}" partId="{cef2d5ce-ed55-4030-a415-e39a20d3ff86}" to="{27fb4823-e857-4e61-a454-a12d30ecfb17}"/>
<edge from="{8c4b6bea-3045-4b62-ba65-442c7c5548b9}" id="{d72ba288-db29-4126-a786-af95ba91ddc2}" partId="{d2be1f6f-2053-491c-962b-f27e1a809848}" to="{8642cce7-c158-4559-99af-bf2caa121348}"/> <edge from="{8c4b6bea-3045-4b62-ba65-442c7c5548b9}" id="{d72ba288-db29-4126-a786-af95ba91ddc2}" partId="{93d5b991-f821-4974-96bd-17c46090ffa9}" to="{8642cce7-c158-4559-99af-bf2caa121348}"/>
<edge from="{f89eea85-45e0-47d7-a009-96e2086d58fc}" id="{de46426b-2eae-4d54-96de-edcd7d78c3b5}" partId="{57622c57-5929-4a6a-b4ac-17dc909f002a}" to="{6186ebb3-84a4-4f2c-9992-b39a3831dea3}"/> <edge from="{f89eea85-45e0-47d7-a009-96e2086d58fc}" id="{de46426b-2eae-4d54-96de-edcd7d78c3b5}" partId="{0a57548e-12d8-4488-9ce4-8fe3b64db253}" to="{6186ebb3-84a4-4f2c-9992-b39a3831dea3}"/>
<edge from="{df5207c8-afdb-4305-b322-33de580b7379}" id="{de74f905-1595-47cc-bc6f-fe10ce731622}" partId="{ad2259a2-362c-499e-9123-d6f8ac0a1966}" to="{2bec4749-11de-442a-862f-8d04d0c590ef}"/> <edge from="{df5207c8-afdb-4305-b322-33de580b7379}" id="{de74f905-1595-47cc-bc6f-fe10ce731622}" partId="{422ad92f-8ad5-402a-9629-2348eb14e103}" to="{2bec4749-11de-442a-862f-8d04d0c590ef}"/>
<edge from="{457564d3-ccd0-465a-8d0b-b57c379666fd}" id="{e277cc73-c658-4704-896e-25ec0bd28d3d}" partId="{57622c57-5929-4a6a-b4ac-17dc909f002a}" to="{f28a9edd-92c5-4e9e-9b69-e63ec9fd7f4f}"/> <edge from="{457564d3-ccd0-465a-8d0b-b57c379666fd}" id="{e277cc73-c658-4704-896e-25ec0bd28d3d}" partId="{0a57548e-12d8-4488-9ce4-8fe3b64db253}" to="{f28a9edd-92c5-4e9e-9b69-e63ec9fd7f4f}"/>
<edge from="{52a3dfd0-0bb7-4869-91b2-85c729e289b0}" id="{e68abb76-b443-44cc-8673-a54dba1a53ee}" partId="{9fae01c2-855a-472b-8181-02a1b76fcab1}" to="{13d47f89-649c-4830-ba6d-fbf7f132793e}"/> <edge from="{52a3dfd0-0bb7-4869-91b2-85c729e289b0}" id="{e68abb76-b443-44cc-8673-a54dba1a53ee}" partId="{b6e20100-8c2b-4550-aee8-806aa713c90c}" to="{13d47f89-649c-4830-ba6d-fbf7f132793e}"/>
<edge from="{e767a69a-e29d-4fbc-8e1d-615a443f40f5}" id="{e81d6458-fb42-4372-b45f-821f4e7869e3}" partId="{2c899cf6-d358-454e-ad79-5a5bddaf0bff}" to="{94c6dd69-3b99-4b25-b6dd-e8d80b579a7e}"/> <edge from="{e767a69a-e29d-4fbc-8e1d-615a443f40f5}" id="{e81d6458-fb42-4372-b45f-821f4e7869e3}" partId="{3145e23e-d0bf-4f3f-8609-92f40c348fa5}" to="{94c6dd69-3b99-4b25-b6dd-e8d80b579a7e}"/>
<edge from="{419c8aca-4bb8-4bab-84e1-214b23a17a27}" id="{ede7f2e3-638d-4e9e-92ad-be7873d93dd4}" partId="{a5ad3f64-2091-4cea-8085-e17881f0573d}" to="{1e7e802c-3711-40c8-9dd6-bcded439cffe}"/> <edge from="{419c8aca-4bb8-4bab-84e1-214b23a17a27}" id="{ede7f2e3-638d-4e9e-92ad-be7873d93dd4}" partId="{cef2d5ce-ed55-4030-a415-e39a20d3ff86}" to="{1e7e802c-3711-40c8-9dd6-bcded439cffe}"/>
<edge from="{2e54f33b-c879-4e40-83cc-75eeefaccdc3}" id="{efe6dea3-8a74-4cb8-8185-b3f81abec92a}" partId="{b27111c8-625f-405d-88a9-55df58d51de0}" to="{37ad0424-3633-48a9-a2ea-bac83d140ec8}"/> <edge from="{2e54f33b-c879-4e40-83cc-75eeefaccdc3}" id="{efe6dea3-8a74-4cb8-8185-b3f81abec92a}" partId="{5f080e1f-48f5-4aa7-aa0c-d175f9045341}" to="{37ad0424-3633-48a9-a2ea-bac83d140ec8}"/>
<edge from="{d2f082e0-13c8-4bc5-8c05-06cea9d46463}" id="{fe31b37d-778b-49a2-8e14-7d6fa7bf62b6}" partId="{fe5e25e8-58cb-43d5-ba72-c64e2cf44d6d}" to="{cb6c4607-f520-4a2e-b282-e54e2cb8165a}"/> <edge from="{d2f082e0-13c8-4bc5-8c05-06cea9d46463}" id="{fe31b37d-778b-49a2-8e14-7d6fa7bf62b6}" partId="{6ff1a4e8-b864-4c5e-8a8e-2d28edd2c55a}" to="{cb6c4607-f520-4a2e-b282-e54e2cb8165a}"/>
<edge from="{f53d85ae-0a06-47aa-8402-b5e0124cff66}" id="{fefb02c4-a3b4-4a66-a909-9c0a44bd56d4}" partId="{881a0adc-3b0d-4575-b7d7-d6df8907e3f3}" to="{6954e176-c739-40d8-b1ef-8ce38884bba2}"/> <edge from="{f53d85ae-0a06-47aa-8402-b5e0124cff66}" id="{fefb02c4-a3b4-4a66-a909-9c0a44bd56d4}" partId="{79289522-791a-488f-bd2d-15db6c8f2f84}" to="{6954e176-c739-40d8-b1ef-8ce38884bba2}"/>
<edge from="{723a0584-b3a1-41a9-a9dc-96b21e733f1f}" id="{ffe15b4f-148a-4f9a-a782-7345734c42cf}" partId="{ad2259a2-362c-499e-9123-d6f8ac0a1966}" to="{2b5dae5c-17e3-4632-ab54-36e21859fbca}"/> <edge from="{723a0584-b3a1-41a9-a9dc-96b21e733f1f}" id="{ffe15b4f-148a-4f9a-a782-7345734c42cf}" partId="{422ad92f-8ad5-402a-9629-2348eb14e103}" to="{2b5dae5c-17e3-4632-ab54-36e21859fbca}"/>
</edges> </edges>
<parts> <parts>
<part chamfered="false" color="#ffb6afb4" disabled="false" id="{019249f8-0f48-4f9e-87e3-6d2e821e87ac}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="false"/> <part chamfered="false" color="#ffb6afb4" disabled="false" id="{0048f320-55a9-4777-914e-95944cb34b46}" locked="false" rounded="false" subdived="true" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ffb6afb4" disabled="false" id="{0b8f8da9-d461-48c4-bbfd-343f0b648235}" locked="false" rounded="false" subdived="true" visible="true" xMirrored="false"/> <part chamfered="false" color="#ffb6afb4" countershaded="true" deformThickness="0.84" disabled="false" id="{0a57548e-12d8-4488-9ce4-8fe3b64db253}" locked="false" rounded="false" subdived="true" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ffb6afb4" disabled="false" id="{15325291-8856-42d7-9bc0-eb9ea53eadc1}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="false"/> <part chamfered="false" color="#ffb6afb4" disabled="false" id="{1f878838-6686-4bcc-bc82-d9c5583d05e8}" locked="false" rounded="false" subdived="true" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ff797477" disabled="false" id="{2c899cf6-d358-454e-ad79-5a5bddaf0bff}" locked="false" rounded="true" subdived="true" visible="true" xMirrored="true"/> <part chamfered="false" color="#ffb6afb4" disabled="false" id="{2162fa67-7e23-4e5f-9a9d-4175b10c44c9}" locked="false" rounded="false" subdived="true" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ffb6afb4" disabled="false" id="{2dacf558-7216-4577-8631-16769f860e18}" locked="false" rounded="false" subdived="true" visible="true" xMirrored="false"/> <part chamfered="false" color="#ff797477" disabled="false" id="{3145e23e-d0bf-4f3f-8609-92f40c348fa5}" locked="false" rounded="true" subdived="true" visible="true" xMirrored="true"/>
<part chamfered="false" color="#ffb6afb4" disabled="false" id="{442a221d-5a04-4d09-9cfd-bdd138320c26}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="false"/> <part chamfered="false" color="#ffb6afb4" disabled="false" id="{3d3ba42b-7be6-4174-96c3-500daab0dc7f}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ffb6afb4" disabled="false" id="{471dfce0-6e13-4146-b261-2090e30f407b}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="false"/> <part chamfered="false" color="#ffb6afb4" countershaded="true" deformWidth="0.82" disabled="false" id="{422ad92f-8ad5-402a-9629-2348eb14e103}" locked="false" rounded="true" subdived="true" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ffb6afb4" disabled="false" id="{546efa0c-392d-47c5-80c1-4138b07cf0a4}" locked="false" rounded="false" subdived="true" visible="true" xMirrored="false"/> <part chamfered="false" color="#ffb6afb4" deformWidth="0.84" disabled="false" id="{5f080e1f-48f5-4aa7-aa0c-d175f9045341}" locked="false" rounded="false" subdived="true" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ffb6afb4" countershaded="true" deformThickness="0.84" disabled="false" id="{57622c57-5929-4a6a-b4ac-17dc909f002a}" locked="false" rounded="false" subdived="true" visible="true" xMirrored="false"/> <part chamfered="false" color="#ff2a292a" disabled="false" id="{62dc13e0-9173-4223-9c39-c682f30a2338}" locked="false" rounded="false" subdived="true" visible="true" xMirrored="true"/>
<part chamfered="false" color="#ffb6afb4" disabled="false" id="{6c3c3e49-1701-4f8c-9490-32d51f52fe9f}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="false"/> <part chamfered="false" color="#ffb6afb4" disabled="false" id="{65aa6ccd-94db-47cf-aa84-dce3c2b3fa65}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ff2a292a" disabled="false" id="{80e078b3-ad1d-4a0f-b695-e63bff35af45}" locked="false" rounded="false" subdived="true" visible="true" xMirrored="true"/> <part chamfered="false" color="#ffb6afb4" countershaded="true" deformThickness="1.16" disabled="false" id="{6ff1a4e8-b864-4c5e-8a8e-2d28edd2c55a}" locked="false" rounded="true" subdived="true" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ffb6afb4" deformWidth="0.84" disabled="false" id="{881a0adc-3b0d-4575-b7d7-d6df8907e3f3}" locked="false" rounded="false" subdived="true" visible="true" xMirrored="false"/> <part chamfered="false" color="#ffb6afb4" deformWidth="0.84" disabled="false" id="{79289522-791a-488f-bd2d-15db6c8f2f84}" locked="false" rounded="false" subdived="true" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ffb6afb4" disabled="false" id="{91850869-d0e1-4e42-a452-d1259dbd5643}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="false"/> <part chamfered="false" color="#ffb6afb4" disabled="false" id="{828c4638-7c11-4aeb-8f23-773dbeea8440}" locked="false" rounded="false" subdived="true" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ffb6afb4" disabled="false" id="{983cf6ce-44d9-41e8-8489-289b423a423a}" locked="false" rounded="false" subdived="true" visible="true" xMirrored="false"/> <part chamfered="false" color="#ffb6afb4" disabled="false" id="{8d55eb65-52a8-47da-9b5b-7d6d914943cc}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ffb6afb4" disabled="false" id="{9fae01c2-855a-472b-8181-02a1b76fcab1}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="false"/> <part chamfered="false" color="#ffb6afb4" deformWidth="0.91" disabled="false" id="{93d5b991-f821-4974-96bd-17c46090ffa9}" locked="false" rounded="true" subdived="true" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ffb6afb4" disabled="false" id="{a5ad3f64-2091-4cea-8085-e17881f0573d}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="false"/> <part chamfered="false" color="#ffb6afb4" deformWidth="0.91" disabled="false" id="{9c9383da-b1ad-4fed-ac7d-e65d9334882f}" locked="false" rounded="true" subdived="true" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ffb6afb4" countershaded="true" deformWidth="0.82" disabled="false" id="{ad2259a2-362c-499e-9123-d6f8ac0a1966}" locked="false" rounded="true" subdived="true" visible="true" xMirrored="false"/> <part chamfered="false" color="#ffb6afb4" disabled="false" id="{aceff516-7672-4ab0-8de4-10fb8c7a0681}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ffb6afb4" deformWidth="0.84" disabled="false" id="{b27111c8-625f-405d-88a9-55df58d51de0}" locked="false" rounded="false" subdived="true" visible="true" xMirrored="false"/> <part chamfered="false" color="#ffb6afb4" disabled="false" id="{b049bb5c-a4e5-402e-88db-a5cccbee3851}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ffb6afb4" disabled="false" id="{c9c2c2b9-571b-4067-8b01-b59725670dd4}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="false"/> <part chamfered="false" color="#ffb6afb4" disabled="false" id="{b6e20100-8c2b-4550-aee8-806aa713c90c}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ffb6afb4" deformWidth="0.91" disabled="false" id="{d2be1f6f-2053-491c-962b-f27e1a809848}" locked="false" rounded="true" subdived="true" visible="true" xMirrored="false"/> <part chamfered="false" color="#ffb6afb4" disabled="false" id="{c2b415b2-4419-408e-be54-1b6b2521441a}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ffb6afb4" disabled="false" id="{d4c0d323-791a-4a2a-ad1b-8e8fb93bf42d}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="false"/> <part chamfered="false" color="#ffb6afb4" disabled="false" id="{c569a92e-6b1b-4d8b-9284-76fef9c124f0}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ffb6afb4" deformWidth="0.91" disabled="false" id="{d5ccb7d6-3357-473c-a26a-12ce5713d92e}" locked="false" rounded="true" subdived="true" visible="true" xMirrored="false"/> <part chamfered="false" color="#ffb6afb4" disabled="false" id="{cef2d5ce-ed55-4030-a415-e39a20d3ff86}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ffb6afb4" cutRotation="0.62" deformWidth="0.15" disabled="false" id="{dd604661-6acc-4ef1-83fc-36a374c54734}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="true"/> <part chamfered="false" color="#ffb6afb4" disabled="false" id="{de6790b2-a8be-42ea-9f96-75c8ac21c6da}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ffb6afb4" countershaded="true" deformThickness="1.16" disabled="false" id="{fe5e25e8-58cb-43d5-ba72-c64e2cf44d6d}" locked="false" rounded="true" subdived="true" visible="true" xMirrored="false"/> <part chamfered="false" color="#ffb6afb4" cutRotation="0.62" deformWidth="0.33" disabled="false" id="{ff29229a-564e-4e50-af37-0ae6c25b78ae}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="true"/>
</parts> </parts>
<components> <components>
<component combineMode="Normal" expanded="false" id="{80825faa-d9b1-4b8d-98fa-85fb3d2b2b35}" linkData="{fe5e25e8-58cb-43d5-ba72-c64e2cf44d6d}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{8b222323-fd8b-4617-aba3-f67f55a6ad95}" linkData="{6ff1a4e8-b864-4c5e-8a8e-2d28edd2c55a}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{2811dd84-ce7d-46fe-becb-a6ece902a2a0}" linkData="{ad2259a2-362c-499e-9123-d6f8ac0a1966}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{1bed0336-0b1c-409d-8f9b-79726eff5bcf}" linkData="{422ad92f-8ad5-402a-9629-2348eb14e103}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{24052f98-6d3f-4299-808e-04a33486056b}" linkData="{57622c57-5929-4a6a-b4ac-17dc909f002a}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{aeceba75-3e1f-4e17-93e0-6bf57a08fc6e}" linkData="{0a57548e-12d8-4488-9ce4-8fe3b64db253}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{9a41ca60-5e2d-491b-8a89-ec55d59c8908}" linkData="{d5ccb7d6-3357-473c-a26a-12ce5713d92e}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{21abd416-ce02-47f6-938d-6e7d35c6d8f4}" linkData="{9c9383da-b1ad-4fed-ac7d-e65d9334882f}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{a58d9d13-6b67-42eb-870a-a1924272602a}" linkData="{d2be1f6f-2053-491c-962b-f27e1a809848}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{51b08def-b3a4-428f-a03d-4125c76bc6ad}" linkData="{93d5b991-f821-4974-96bd-17c46090ffa9}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{46543657-43b0-4460-8c48-c0957c0434b7}" linkData="{b27111c8-625f-405d-88a9-55df58d51de0}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{86ff1c3a-175e-4450-8c10-0523794e0cf4}" linkData="{5f080e1f-48f5-4aa7-aa0c-d175f9045341}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{d11f248d-dfb7-4298-af9e-dc60a9fb0011}" linkData="{881a0adc-3b0d-4575-b7d7-d6df8907e3f3}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{162ed54e-3062-4c63-93b3-8189b0bb1028}" linkData="{79289522-791a-488f-bd2d-15db6c8f2f84}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{8763787f-7e83-44b0-80ab-71fd10936fa9}" linkData="{983cf6ce-44d9-41e8-8489-289b423a423a}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{50d83758-21df-40f6-a804-68b8ca1301f1}" linkData="{1f878838-6686-4bcc-bc82-d9c5583d05e8}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{61dcbceb-6e49-47b1-ac07-209b16ef1cb1}" linkData="{0b8f8da9-d461-48c4-bbfd-343f0b648235}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{026fdbd1-1a08-4008-82db-2ca3de67f429}" linkData="{0048f320-55a9-4777-914e-95944cb34b46}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{bd2d596d-0477-47f8-8083-1def956cb7fd}" linkData="{c9c2c2b9-571b-4067-8b01-b59725670dd4}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{c1dd1e40-469a-4e32-b8fe-ba90d553b3ca}" linkData="{c569a92e-6b1b-4d8b-9284-76fef9c124f0}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{b8e478cc-e2cb-4e6a-956b-a4145355d2e2}" linkData="{546efa0c-392d-47c5-80c1-4138b07cf0a4}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{b29dba15-f2d9-4afa-94d7-7c027bcf27dd}" linkData="{2162fa67-7e23-4e5f-9a9d-4175b10c44c9}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{5e744fdf-98b0-4571-95b5-ba05747a30a7}" linkData="{2dacf558-7216-4577-8631-16769f860e18}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{b2c413da-9043-4e6a-9a8e-abb9a126f54c}" linkData="{828c4638-7c11-4aeb-8f23-773dbeea8440}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{9d280cd2-7391-405b-bb86-32f7131dc566}" linkData="{9fae01c2-855a-472b-8181-02a1b76fcab1}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{53d42cdd-93f8-4a41-925e-0dcbe373f5e3}" linkData="{b6e20100-8c2b-4550-aee8-806aa713c90c}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{a5b46483-8649-4973-8f2e-bc1dd66fd9bd}" linkData="{6c3c3e49-1701-4f8c-9490-32d51f52fe9f}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{fe6fe973-2958-4cb6-b8cc-1c8c20c72b45}" linkData="{b049bb5c-a4e5-402e-88db-a5cccbee3851}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{8a20c19b-ea41-42b5-9ffe-f465682ab8f5}" linkData="{15325291-8856-42d7-9bc0-eb9ea53eadc1}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{145d43d4-9b5c-4791-887b-21f318b6d190}" linkData="{3d3ba42b-7be6-4174-96c3-500daab0dc7f}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{202b5c05-5435-461e-8777-b9dff62619c1}" linkData="{471dfce0-6e13-4146-b261-2090e30f407b}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{9e2d24be-642a-4f87-b9c7-a66bcb1baf1c}" linkData="{aceff516-7672-4ab0-8de4-10fb8c7a0681}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{aeefae79-b1bf-477f-9f4c-54b4f973766f}" linkData="{a5ad3f64-2091-4cea-8085-e17881f0573d}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{37d27813-02cd-479e-8de2-e6ffc6b18dbf}" linkData="{cef2d5ce-ed55-4030-a415-e39a20d3ff86}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{dba2df2e-92ff-4221-a5e0-4af930c10a42}" linkData="{91850869-d0e1-4e42-a452-d1259dbd5643}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{96323358-10dc-4fe9-a6f4-7aee4fe074bd}" linkData="{c2b415b2-4419-408e-be54-1b6b2521441a}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{c8392698-b8cf-4dcd-a72c-2617bd83e920}" linkData="{019249f8-0f48-4f9e-87e3-6d2e821e87ac}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{5b17a1e6-b837-429f-ae51-89f45874ea4f}" linkData="{65aa6ccd-94db-47cf-aa84-dce3c2b3fa65}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{53c9e72d-a221-4ecf-9bb0-08a31625a5ec}" linkData="{442a221d-5a04-4d09-9cfd-bdd138320c26}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{f0b7a33a-6c96-400d-9800-6d9f75edc099}" linkData="{de6790b2-a8be-42ea-9f96-75c8ac21c6da}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{7432db30-498e-438c-b6dc-69a16f18af88}" linkData="{d4c0d323-791a-4a2a-ad1b-8e8fb93bf42d}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{5c57715a-8616-4cf6-b2c2-3bf5ce50b53d}" linkData="{8d55eb65-52a8-47da-9b5b-7d6d914943cc}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{734b21ee-2a7b-4f0d-a087-3c5915b3a6ef}" linkData="{2c899cf6-d358-454e-ad79-5a5bddaf0bff}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{3ff1b8d2-c944-4578-b532-acce0683303b}" linkData="{3145e23e-d0bf-4f3f-8609-92f40c348fa5}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{2e9234a4-ec4c-4621-aa83-8eb078c94fe0}" linkData="{dd604661-6acc-4ef1-83fc-36a374c54734}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{dc08b366-9adb-48fd-bc0e-26b147e7e4a6}" linkData="{ff29229a-564e-4e50-af37-0ae6c25b78ae}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{921b72f3-9d6a-4d78-b1f0-528bc4a119d5}" linkData="{80e078b3-ad1d-4a0f-b695-e63bff35af45}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{c967697f-b119-4664-989c-3aeb6bf35f4f}" linkData="{62dc13e0-9173-4223-9c39-c682f30a2338}" linkDataType="partId"/>
</components> </components>
<materials/> <materials/>
<poses/> <poses/>

View File

@ -1,102 +1,102 @@
DUST3D 1.0 xml 0000000194 DUST3D 1.0 xml 0000000194
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<ds3> <ds3>
<model name="model.xml" offset="0" size="18526"/> <model name="model.xml" offset="0" size="18527"/>
<asset name="canvas.png" offset="18526" size="1461880"/> <asset name="canvas.png" offset="18527" size="1461880"/>
</ds3> </ds3>
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<canvas originX="2.30357" originY="0.250001" originZ="5.33631" polyCount="TremendouslyHighPoly" rigType="Animal"> <canvas originX="2.30357" originY="0.250001" originZ="5.33631" polyCount="TremendouslyHighPoly" rigType="Animal">
<nodes> <nodes>
<node id="{002d2643-ad37-4b31-9427-ffa80d75cef0}" partId="{c30b83af-faa5-4697-88f4-145eccc4a141}" radius="0.130952" x="2.30952" y="0.14881" z="5.06548"/> <node id="{002d2643-ad37-4b31-9427-ffa80d75cef0}" partId="{b8b3ef86-c3fb-413e-81b2-22704d2c9bde}" radius="0.149644" x="2.30952" y="0.14881" z="5.06548"/>
<node id="{0773edbe-d60e-4df9-ac1a-b228f69aeb9e}" partId="{af705abc-552c-43b7-9518-5c299bbbaefe}" radius="0.0178571" x="2.24242" y="0.277906" z="5.24721"/> <node id="{0773edbe-d60e-4df9-ac1a-b228f69aeb9e}" partId="{1933a268-77e5-4c9d-9a0e-81adb51be9cf}" radius="0.0178571" x="2.24242" y="0.277906" z="5.24721"/>
<node id="{0c6e2b32-45d5-4c33-8f41-f8d259f3bfaf}" partId="{af705abc-552c-43b7-9518-5c299bbbaefe}" radius="0.00595238" x="2.24242" y="0.330289" z="5.25834"/> <node id="{0c6e2b32-45d5-4c33-8f41-f8d259f3bfaf}" partId="{1933a268-77e5-4c9d-9a0e-81adb51be9cf}" radius="0.00595238" x="2.24242" y="0.330289" z="5.25834"/>
<node id="{105c0e26-729b-4f41-9c57-5cc6a8feb5c4}" partId="{a0fd6b1b-efc3-4211-a439-4e87d2fdb3f9}" radius="0.0892857" x="2.30357" y="0.232143" z="5.96429"/> <node id="{105c0e26-729b-4f41-9c57-5cc6a8feb5c4}" partId="{ae61b00a-6fa7-4d27-9b78-43f87bac6823}" radius="0.0892857" x="2.30357" y="0.232143" z="5.96429"/>
<node id="{2be2d852-dc7e-4082-b3bd-fb7c7b67cd9e}" partId="{c30b83af-faa5-4697-88f4-145eccc4a141}" radius="0.142857" x="2.30952" y="0.172619" z="5.20833"/> <node id="{2be2d852-dc7e-4082-b3bd-fb7c7b67cd9e}" partId="{b8b3ef86-c3fb-413e-81b2-22704d2c9bde}" radius="0.142857" x="2.30952" y="0.172619" z="5.20833"/>
<node boneMark="Limb" id="{2cd26254-2af2-419b-944d-7ec4d20ccb49}" partId="{78b71f39-5010-4771-8d90-aaa08f38aa7a}" radius="0.232143" x="2.22619" y="0.130952" z="5.11607"/> <node boneMark="Limb" id="{2cd26254-2af2-419b-944d-7ec4d20ccb49}" partId="{f5133a44-dda9-4189-bba6-7d291eb5453e}" radius="0.232143" x="2.22619" y="0.130952" z="5.11607"/>
<node id="{31955658-3734-4b40-935e-5ab565b1a031}" partId="{60038150-9349-422c-96b1-cead9d97ab73}" radius="0.0119048" x="2.3671" y="0.107444" z="4.75297"/> <node id="{31955658-3734-4b40-935e-5ab565b1a031}" partId="{52600dec-a5db-4d32-b175-fef8168d9f69}" radius="0.0119048" x="2.3671" y="0.107444" z="4.75297"/>
<node id="{33482357-a9fb-43fd-971d-13e6cba6923a}" partId="{92f4278b-354e-45fb-83ea-6464be1d3309}" radius="0.0238095" x="2.30357" y="0.154762" z="4.6994"/> <node id="{33482357-a9fb-43fd-971d-13e6cba6923a}" partId="{a5feecf2-e126-4034-8cde-597ad5d0e412}" radius="0.0238095" x="2.30357" y="0.154762" z="4.6994"/>
<node id="{3ffe9f73-2f15-492c-9049-3ec5a11c13e5}" partId="{c30b83af-faa5-4697-88f4-145eccc4a141}" radius="0.0833333" x="2.30952" y="0.220238" z="5.57738"/> <node id="{3ffe9f73-2f15-492c-9049-3ec5a11c13e5}" partId="{b8b3ef86-c3fb-413e-81b2-22704d2c9bde}" radius="0.0833333" x="2.30952" y="0.220238" z="5.57738"/>
<node id="{478ce079-24b3-49d2-b627-d1f930324acf}" partId="{78b71f39-5010-4771-8d90-aaa08f38aa7a}" radius="0.232143" x="1.94048" y="0.273809" z="5.15178"/> <node id="{478ce079-24b3-49d2-b627-d1f930324acf}" partId="{f5133a44-dda9-4189-bba6-7d291eb5453e}" radius="0.232143" x="1.94048" y="0.273809" z="5.15178"/>
<node id="{55364285-9fc5-447b-980d-8a7b9c6fb20c}" partId="{a0fd6b1b-efc3-4211-a439-4e87d2fdb3f9}" radius="0.0654762" x="2.30357" y="0.214286" z="5.80952"/> <node id="{55364285-9fc5-447b-980d-8a7b9c6fb20c}" partId="{ae61b00a-6fa7-4d27-9b78-43f87bac6823}" radius="0.0654762" x="2.30357" y="0.214286" z="5.80952"/>
<node id="{577919f7-ebd4-4d95-954a-f1e53757bf7c}" partId="{92f4278b-354e-45fb-83ea-6464be1d3309}" radius="0.005" x="2.30952" y="0.190476" z="4.57738"/> <node id="{577919f7-ebd4-4d95-954a-f1e53757bf7c}" partId="{a5feecf2-e126-4034-8cde-597ad5d0e412}" radius="0.005" x="2.30952" y="0.190476" z="4.57738"/>
<node id="{60e0a638-397d-4ae5-ac21-bc6c932e36f6}" partId="{aee65af2-6917-4e38-a03d-08243b9d9e24}" radius="0.0357143" x="2.30357" y="0.154762" z="4.69643"/> <node id="{60e0a638-397d-4ae5-ac21-bc6c932e36f6}" partId="{74ab9ba2-f9d5-4d65-abb0-d6f1ae73b253}" radius="0.0357143" x="2.30357" y="0.154762" z="4.69643"/>
<node id="{64fd22d8-d4e9-49f2-84d2-a7dc883aac3e}" partId="{af705abc-552c-43b7-9518-5c299bbbaefe}" radius="0.00595238" x="2.25216" y="0.396958" z="5.27251"/> <node id="{64fd22d8-d4e9-49f2-84d2-a7dc883aac3e}" partId="{1933a268-77e5-4c9d-9a0e-81adb51be9cf}" radius="0.00595238" x="2.25216" y="0.396958" z="5.27251"/>
<node boneMark="Tail" id="{65894105-a88a-4946-9da0-dcc34c3d1201}" partId="{a0fd6b1b-efc3-4211-a439-4e87d2fdb3f9}" radius="0.0654762" x="2.30357" y="0.220238" z="5.67857"/> <node boneMark="Tail" id="{65894105-a88a-4946-9da0-dcc34c3d1201}" partId="{ae61b00a-6fa7-4d27-9b78-43f87bac6823}" radius="0.0654762" x="2.30357" y="0.220238" z="5.67857"/>
<node id="{6a5c5722-8801-4958-98e2-a39fff2fcedb}" partId="{a0fd6b1b-efc3-4211-a439-4e87d2fdb3f9}" radius="0.0892857" x="2.30357" y="0.22619" z="5.58036"/> <node id="{6a5c5722-8801-4958-98e2-a39fff2fcedb}" partId="{ae61b00a-6fa7-4d27-9b78-43f87bac6823}" radius="0.0892857" x="2.30357" y="0.22619" z="5.58036"/>
<node id="{72e92d06-1c01-43cb-8de3-ec6f9a74301d}" partId="{c30b83af-faa5-4697-88f4-145eccc4a141}" radius="0.107143" x="2.30952" y="0.125" z="4.91667"/> <node id="{72e92d06-1c01-43cb-8de3-ec6f9a74301d}" partId="{b8b3ef86-c3fb-413e-81b2-22704d2c9bde}" radius="0.125835" x="2.30952" y="0.125" z="4.91667"/>
<node id="{768d8ac5-7c00-4faf-9a4f-13993f69213a}" partId="{bd1b2d09-62da-44c7-a0d0-d37f9943eaa7}" radius="0.0178571" x="2.99405" y="0.958333" z="1.59226"/> <node id="{768d8ac5-7c00-4faf-9a4f-13993f69213a}" partId="{62d7ef5f-8f47-4aeb-bb39-97da75e181ea}" radius="0.0178571" x="3.04762" y="0.958333" z="1.59226"/>
<node id="{8486ca8d-6418-427f-b3cd-9dc4cc8b8308}" partId="{c30b83af-faa5-4697-88f4-145eccc4a141}" radius="0.107143" x="2.30952" y="0.214286" z="5.50595"/> <node id="{8486ca8d-6418-427f-b3cd-9dc4cc8b8308}" partId="{b8b3ef86-c3fb-413e-81b2-22704d2c9bde}" radius="0.107143" x="2.30952" y="0.214286" z="5.50595"/>
<node id="{86225f61-4a1a-4675-a8a4-befd428fcdd8}" partId="{c30b83af-faa5-4697-88f4-145eccc4a141}" radius="0.0833333" x="2.30952" y="0.107143" z="4.83036"/> <node id="{86225f61-4a1a-4675-a8a4-befd428fcdd8}" partId="{b8b3ef86-c3fb-413e-81b2-22704d2c9bde}" radius="0.0833333" x="2.30952" y="0.107143" z="4.83036"/>
<node id="{95c7520f-6d00-4b48-a2b3-7e20e58c9388}" partId="{af705abc-552c-43b7-9518-5c299bbbaefe}" radius="0.005" x="2.22728" y="0.452192" z="5.38419"/> <node id="{95c7520f-6d00-4b48-a2b3-7e20e58c9388}" partId="{1933a268-77e5-4c9d-9a0e-81adb51be9cf}" radius="0.005" x="2.22728" y="0.452192" z="5.38419"/>
<node id="{9f38a9ee-e7e5-4e63-8d55-0623ca9ef5f2}" partId="{c30b83af-faa5-4697-88f4-145eccc4a141}" radius="0.142857" x="2.30952" y="0.214286" z="5.3631"/> <node id="{9f38a9ee-e7e5-4e63-8d55-0623ca9ef5f2}" partId="{b8b3ef86-c3fb-413e-81b2-22704d2c9bde}" radius="0.142857" x="2.30952" y="0.214286" z="5.3631"/>
<node id="{a7c6d064-d5ad-48c7-b508-ad6f7de3a330}" partId="{bd1b2d09-62da-44c7-a0d0-d37f9943eaa7}" radius="0.0178571" x="3.00595" y="0.642857" z="1.59226"/> <node id="{a7c6d064-d5ad-48c7-b508-ad6f7de3a330}" partId="{62d7ef5f-8f47-4aeb-bb39-97da75e181ea}" radius="0.0178571" x="3.03572" y="0.642857" z="1.59226"/>
<node id="{ad81ec2b-2ab8-41e1-b044-a992b9e670c9}" partId="{bd1b2d09-62da-44c7-a0d0-d37f9943eaa7}" radius="0.0178571" x="3.09524" y="0.541667" z="1.59226"/> <node id="{ad81ec2b-2ab8-41e1-b044-a992b9e670c9}" partId="{62d7ef5f-8f47-4aeb-bb39-97da75e181ea}" radius="0.0178571" x="2.94643" y="0.541667" z="1.59226"/>
<node boneMark="Neck" id="{b35647e5-b5f2-431c-b928-f13b17ecce62}" partId="{aee65af2-6917-4e38-a03d-08243b9d9e24}" radius="0.0952381" x="2.30357" y="0.125" z="4.84821"/> <node boneMark="Neck" id="{b35647e5-b5f2-431c-b928-f13b17ecce62}" partId="{74ab9ba2-f9d5-4d65-abb0-d6f1ae73b253}" radius="0.0952381" x="2.30357" y="0.125" z="4.84821"/>
<node id="{ba8340d9-94c0-4d00-924f-1c7e955e933a}" partId="{af705abc-552c-43b7-9518-5c299bbbaefe}" radius="0.0178571" x="2.24676" y="0.437208" z="5.34301"/> <node id="{ba8340d9-94c0-4d00-924f-1c7e955e933a}" partId="{1933a268-77e5-4c9d-9a0e-81adb51be9cf}" radius="0.0178571" x="2.24676" y="0.437208" z="5.34301"/>
<node id="{c1bfe566-ede8-451c-aff5-67440421761f}" partId="{92f4278b-354e-45fb-83ea-6464be1d3309}" radius="0.0119048" x="2.30952" y="0.160714" z="4.6369"/> <node id="{c1bfe566-ede8-451c-aff5-67440421761f}" partId="{a5feecf2-e126-4034-8cde-597ad5d0e412}" radius="0.0119048" x="2.30952" y="0.160714" z="4.6369"/>
<node id="{c1ef7958-3e56-40cb-88b2-5c3baaa8dda9}" partId="{78b71f39-5010-4771-8d90-aaa08f38aa7a}" radius="0.077381" x="1.51191" y="0.666668" z="5.32143"/> <node id="{c1ef7958-3e56-40cb-88b2-5c3baaa8dda9}" partId="{f5133a44-dda9-4189-bba6-7d291eb5453e}" radius="0.077381" x="1.51191" y="0.666668" z="5.32143"/>
<node boneMark="Joint" id="{c95cfb16-56f5-4e55-b96f-685ef3735e33}" partId="{78b71f39-5010-4771-8d90-aaa08f38aa7a}" radius="0.0654762" x="1.39286" y="0.75" z="5.39285"/> <node boneMark="Joint" id="{c95cfb16-56f5-4e55-b96f-685ef3735e33}" partId="{f5133a44-dda9-4189-bba6-7d291eb5453e}" radius="0.0654762" x="1.39286" y="0.75" z="5.39285"/>
<node boneMark="Joint" id="{cdaad2ba-c614-412e-a1c6-b9da2bab2162}" partId="{78b71f39-5010-4771-8d90-aaa08f38aa7a}" radius="0.232143" x="1.8631" y="0.398809" z="5.23214"/> <node boneMark="Joint" id="{cdaad2ba-c614-412e-a1c6-b9da2bab2162}" partId="{f5133a44-dda9-4189-bba6-7d291eb5453e}" radius="0.232143" x="1.8631" y="0.398809" z="5.23214"/>
<node id="{d865109f-5575-4663-8d0b-e41776fd0c9a}" partId="{bd1b2d09-62da-44c7-a0d0-d37f9943eaa7}" radius="0.0178571" x="2.9881" y="0.815476" z="1.59226"/> <node id="{d865109f-5575-4663-8d0b-e41776fd0c9a}" partId="{62d7ef5f-8f47-4aeb-bb39-97da75e181ea}" radius="0.0178571" x="3.05357" y="0.815476" z="1.59226"/>
<node id="{e2aa74db-2711-46ab-bf58-203cf02c7e12}" partId="{60038150-9349-422c-96b1-cead9d97ab73}" radius="0.0119048" x="2.33527" y="0.124699" z="4.71726"/> <node id="{e2aa74db-2711-46ab-bf58-203cf02c7e12}" partId="{52600dec-a5db-4d32-b175-fef8168d9f69}" radius="0.0119048" x="2.33527" y="0.124699" z="4.71726"/>
<node id="{e4e4496a-af11-45f1-b14b-f83ccf13138d}" partId="{78b71f39-5010-4771-8d90-aaa08f38aa7a}" radius="0.14881" x="1.6131" y="0.54762" z="5.2619"/> <node id="{e4e4496a-af11-45f1-b14b-f83ccf13138d}" partId="{f5133a44-dda9-4189-bba6-7d291eb5453e}" radius="0.14881" x="1.6131" y="0.54762" z="5.2619"/>
<node id="{ea5e8c43-8d6e-483b-b68e-1bba762c75f2}" partId="{af705abc-552c-43b7-9518-5c299bbbaefe}" radius="0.0178571" x="2.24729" y="0.420925" z="5.29829"/> <node id="{ea5e8c43-8d6e-483b-b68e-1bba762c75f2}" partId="{1933a268-77e5-4c9d-9a0e-81adb51be9cf}" radius="0.0178571" x="2.24729" y="0.420925" z="5.29829"/>
<node id="{ec6f812c-2def-4efc-be46-f00e8ea5d53c}" partId="{a0fd6b1b-efc3-4211-a439-4e87d2fdb3f9}" radius="0.0892857" x="2.30357" y="0.214286" z="5.89881"/> <node id="{ec6f812c-2def-4efc-be46-f00e8ea5d53c}" partId="{ae61b00a-6fa7-4d27-9b78-43f87bac6823}" radius="0.0892857" x="2.30357" y="0.214286" z="5.89881"/>
<node id="{fab13514-17e7-4196-b109-469dceca460b}" partId="{aee65af2-6917-4e38-a03d-08243b9d9e24}" radius="0.0595238" x="2.30357" y="0.136905" z="4.7619"/> <node id="{fab13514-17e7-4196-b109-469dceca460b}" partId="{74ab9ba2-f9d5-4d65-abb0-d6f1ae73b253}" radius="0.0595238" x="2.30357" y="0.136905" z="4.7619"/>
</nodes> </nodes>
<edges> <edges>
<edge from="{e4e4496a-af11-45f1-b14b-f83ccf13138d}" id="{079c1f09-1fd7-4dcb-bdf0-0bfc96d74fa6}" partId="{78b71f39-5010-4771-8d90-aaa08f38aa7a}" to="{c1ef7958-3e56-40cb-88b2-5c3baaa8dda9}"/> <edge from="{e4e4496a-af11-45f1-b14b-f83ccf13138d}" id="{079c1f09-1fd7-4dcb-bdf0-0bfc96d74fa6}" partId="{f5133a44-dda9-4189-bba6-7d291eb5453e}" to="{c1ef7958-3e56-40cb-88b2-5c3baaa8dda9}"/>
<edge from="{ad81ec2b-2ab8-41e1-b044-a992b9e670c9}" id="{1fbc1f23-79c9-4316-9f26-9097216e63dd}" partId="{bd1b2d09-62da-44c7-a0d0-d37f9943eaa7}" to="{a7c6d064-d5ad-48c7-b508-ad6f7de3a330}"/> <edge from="{ad81ec2b-2ab8-41e1-b044-a992b9e670c9}" id="{1fbc1f23-79c9-4316-9f26-9097216e63dd}" partId="{62d7ef5f-8f47-4aeb-bb39-97da75e181ea}" to="{a7c6d064-d5ad-48c7-b508-ad6f7de3a330}"/>
<edge from="{33482357-a9fb-43fd-971d-13e6cba6923a}" id="{292931fb-346b-4453-a368-db9af68b5463}" partId="{92f4278b-354e-45fb-83ea-6464be1d3309}" to="{c1bfe566-ede8-451c-aff5-67440421761f}"/> <edge from="{33482357-a9fb-43fd-971d-13e6cba6923a}" id="{292931fb-346b-4453-a368-db9af68b5463}" partId="{a5feecf2-e126-4034-8cde-597ad5d0e412}" to="{c1bfe566-ede8-451c-aff5-67440421761f}"/>
<edge from="{fab13514-17e7-4196-b109-469dceca460b}" id="{3528cf96-8957-4719-b1a4-404e58e0dac0}" partId="{aee65af2-6917-4e38-a03d-08243b9d9e24}" to="{60e0a638-397d-4ae5-ac21-bc6c932e36f6}"/> <edge from="{fab13514-17e7-4196-b109-469dceca460b}" id="{3528cf96-8957-4719-b1a4-404e58e0dac0}" partId="{74ab9ba2-f9d5-4d65-abb0-d6f1ae73b253}" to="{60e0a638-397d-4ae5-ac21-bc6c932e36f6}"/>
<edge from="{0c6e2b32-45d5-4c33-8f41-f8d259f3bfaf}" id="{3dab5592-ed48-4ae3-8501-6f9925309c3e}" partId="{af705abc-552c-43b7-9518-5c299bbbaefe}" to="{64fd22d8-d4e9-49f2-84d2-a7dc883aac3e}"/> <edge from="{0c6e2b32-45d5-4c33-8f41-f8d259f3bfaf}" id="{3dab5592-ed48-4ae3-8501-6f9925309c3e}" partId="{1933a268-77e5-4c9d-9a0e-81adb51be9cf}" to="{64fd22d8-d4e9-49f2-84d2-a7dc883aac3e}"/>
<edge from="{86225f61-4a1a-4675-a8a4-befd428fcdd8}" id="{41b5d109-26bd-49d3-b096-dec144e887c4}" partId="{c30b83af-faa5-4697-88f4-145eccc4a141}" to="{72e92d06-1c01-43cb-8de3-ec6f9a74301d}"/> <edge from="{86225f61-4a1a-4675-a8a4-befd428fcdd8}" id="{41b5d109-26bd-49d3-b096-dec144e887c4}" partId="{b8b3ef86-c3fb-413e-81b2-22704d2c9bde}" to="{72e92d06-1c01-43cb-8de3-ec6f9a74301d}"/>
<edge from="{65894105-a88a-4946-9da0-dcc34c3d1201}" id="{47a9be10-963b-434b-a04d-4663f96dfaf8}" partId="{a0fd6b1b-efc3-4211-a439-4e87d2fdb3f9}" to="{55364285-9fc5-447b-980d-8a7b9c6fb20c}"/> <edge from="{65894105-a88a-4946-9da0-dcc34c3d1201}" id="{47a9be10-963b-434b-a04d-4663f96dfaf8}" partId="{ae61b00a-6fa7-4d27-9b78-43f87bac6823}" to="{55364285-9fc5-447b-980d-8a7b9c6fb20c}"/>
<edge from="{cdaad2ba-c614-412e-a1c6-b9da2bab2162}" id="{543a1de2-1a71-43d7-aa3a-ea21a2d7538f}" partId="{78b71f39-5010-4771-8d90-aaa08f38aa7a}" to="{e4e4496a-af11-45f1-b14b-f83ccf13138d}"/> <edge from="{cdaad2ba-c614-412e-a1c6-b9da2bab2162}" id="{543a1de2-1a71-43d7-aa3a-ea21a2d7538f}" partId="{f5133a44-dda9-4189-bba6-7d291eb5453e}" to="{e4e4496a-af11-45f1-b14b-f83ccf13138d}"/>
<edge from="{64fd22d8-d4e9-49f2-84d2-a7dc883aac3e}" id="{5656a96e-8bce-4bc8-83a4-bb57ce5b70ec}" partId="{af705abc-552c-43b7-9518-5c299bbbaefe}" to="{ea5e8c43-8d6e-483b-b68e-1bba762c75f2}"/> <edge from="{64fd22d8-d4e9-49f2-84d2-a7dc883aac3e}" id="{5656a96e-8bce-4bc8-83a4-bb57ce5b70ec}" partId="{1933a268-77e5-4c9d-9a0e-81adb51be9cf}" to="{ea5e8c43-8d6e-483b-b68e-1bba762c75f2}"/>
<edge from="{e2aa74db-2711-46ab-bf58-203cf02c7e12}" id="{699740c9-d498-496d-8ca8-d022404430e3}" partId="{60038150-9349-422c-96b1-cead9d97ab73}" to="{31955658-3734-4b40-935e-5ab565b1a031}"/> <edge from="{e2aa74db-2711-46ab-bf58-203cf02c7e12}" id="{699740c9-d498-496d-8ca8-d022404430e3}" partId="{52600dec-a5db-4d32-b175-fef8168d9f69}" to="{31955658-3734-4b40-935e-5ab565b1a031}"/>
<edge from="{6a5c5722-8801-4958-98e2-a39fff2fcedb}" id="{729ef5a4-b2ff-45d0-a59f-c7321b2408a1}" partId="{a0fd6b1b-efc3-4211-a439-4e87d2fdb3f9}" to="{65894105-a88a-4946-9da0-dcc34c3d1201}"/> <edge from="{6a5c5722-8801-4958-98e2-a39fff2fcedb}" id="{729ef5a4-b2ff-45d0-a59f-c7321b2408a1}" partId="{ae61b00a-6fa7-4d27-9b78-43f87bac6823}" to="{65894105-a88a-4946-9da0-dcc34c3d1201}"/>
<edge from="{ea5e8c43-8d6e-483b-b68e-1bba762c75f2}" id="{7460d47d-58f1-420d-9a2d-8abcc2b77b20}" partId="{af705abc-552c-43b7-9518-5c299bbbaefe}" to="{ba8340d9-94c0-4d00-924f-1c7e955e933a}"/> <edge from="{ea5e8c43-8d6e-483b-b68e-1bba762c75f2}" id="{7460d47d-58f1-420d-9a2d-8abcc2b77b20}" partId="{1933a268-77e5-4c9d-9a0e-81adb51be9cf}" to="{ba8340d9-94c0-4d00-924f-1c7e955e933a}"/>
<edge from="{55364285-9fc5-447b-980d-8a7b9c6fb20c}" id="{7f1d6db0-502e-48f3-9f44-ac626a48b869}" partId="{a0fd6b1b-efc3-4211-a439-4e87d2fdb3f9}" to="{ec6f812c-2def-4efc-be46-f00e8ea5d53c}"/> <edge from="{55364285-9fc5-447b-980d-8a7b9c6fb20c}" id="{7f1d6db0-502e-48f3-9f44-ac626a48b869}" partId="{ae61b00a-6fa7-4d27-9b78-43f87bac6823}" to="{ec6f812c-2def-4efc-be46-f00e8ea5d53c}"/>
<edge from="{9f38a9ee-e7e5-4e63-8d55-0623ca9ef5f2}" id="{80b4c3cd-45b5-456f-87e6-39de48ac76e1}" partId="{c30b83af-faa5-4697-88f4-145eccc4a141}" to="{8486ca8d-6418-427f-b3cd-9dc4cc8b8308}"/> <edge from="{9f38a9ee-e7e5-4e63-8d55-0623ca9ef5f2}" id="{80b4c3cd-45b5-456f-87e6-39de48ac76e1}" partId="{b8b3ef86-c3fb-413e-81b2-22704d2c9bde}" to="{8486ca8d-6418-427f-b3cd-9dc4cc8b8308}"/>
<edge from="{2cd26254-2af2-419b-944d-7ec4d20ccb49}" id="{8132f215-3d6d-46f6-bc4b-140341098298}" partId="{78b71f39-5010-4771-8d90-aaa08f38aa7a}" to="{478ce079-24b3-49d2-b627-d1f930324acf}"/> <edge from="{2cd26254-2af2-419b-944d-7ec4d20ccb49}" id="{8132f215-3d6d-46f6-bc4b-140341098298}" partId="{f5133a44-dda9-4189-bba6-7d291eb5453e}" to="{478ce079-24b3-49d2-b627-d1f930324acf}"/>
<edge from="{478ce079-24b3-49d2-b627-d1f930324acf}" id="{8718b4ff-7004-4f54-9ef2-ebcb2aef2cb5}" partId="{78b71f39-5010-4771-8d90-aaa08f38aa7a}" to="{cdaad2ba-c614-412e-a1c6-b9da2bab2162}"/> <edge from="{478ce079-24b3-49d2-b627-d1f930324acf}" id="{8718b4ff-7004-4f54-9ef2-ebcb2aef2cb5}" partId="{f5133a44-dda9-4189-bba6-7d291eb5453e}" to="{cdaad2ba-c614-412e-a1c6-b9da2bab2162}"/>
<edge from="{ba8340d9-94c0-4d00-924f-1c7e955e933a}" id="{88cff3cc-13ea-41ca-bc2c-da7871696d23}" partId="{af705abc-552c-43b7-9518-5c299bbbaefe}" to="{95c7520f-6d00-4b48-a2b3-7e20e58c9388}"/> <edge from="{ba8340d9-94c0-4d00-924f-1c7e955e933a}" id="{88cff3cc-13ea-41ca-bc2c-da7871696d23}" partId="{1933a268-77e5-4c9d-9a0e-81adb51be9cf}" to="{95c7520f-6d00-4b48-a2b3-7e20e58c9388}"/>
<edge from="{002d2643-ad37-4b31-9427-ffa80d75cef0}" id="{88d7324c-1c6a-4282-b935-0853da418023}" partId="{c30b83af-faa5-4697-88f4-145eccc4a141}" to="{2be2d852-dc7e-4082-b3bd-fb7c7b67cd9e}"/> <edge from="{002d2643-ad37-4b31-9427-ffa80d75cef0}" id="{88d7324c-1c6a-4282-b935-0853da418023}" partId="{b8b3ef86-c3fb-413e-81b2-22704d2c9bde}" to="{2be2d852-dc7e-4082-b3bd-fb7c7b67cd9e}"/>
<edge from="{b35647e5-b5f2-431c-b928-f13b17ecce62}" id="{8a14bac1-b29a-4c47-ae52-701c20debd3e}" partId="{aee65af2-6917-4e38-a03d-08243b9d9e24}" to="{fab13514-17e7-4196-b109-469dceca460b}"/> <edge from="{b35647e5-b5f2-431c-b928-f13b17ecce62}" id="{8a14bac1-b29a-4c47-ae52-701c20debd3e}" partId="{74ab9ba2-f9d5-4d65-abb0-d6f1ae73b253}" to="{fab13514-17e7-4196-b109-469dceca460b}"/>
<edge from="{a7c6d064-d5ad-48c7-b508-ad6f7de3a330}" id="{9f5eff50-c80b-4aa2-b0ef-6b7c2352db74}" partId="{bd1b2d09-62da-44c7-a0d0-d37f9943eaa7}" to="{d865109f-5575-4663-8d0b-e41776fd0c9a}"/> <edge from="{a7c6d064-d5ad-48c7-b508-ad6f7de3a330}" id="{9f5eff50-c80b-4aa2-b0ef-6b7c2352db74}" partId="{62d7ef5f-8f47-4aeb-bb39-97da75e181ea}" to="{d865109f-5575-4663-8d0b-e41776fd0c9a}"/>
<edge from="{8486ca8d-6418-427f-b3cd-9dc4cc8b8308}" id="{af8ae996-ffef-4c28-ab35-7bad88460ad0}" partId="{c30b83af-faa5-4697-88f4-145eccc4a141}" to="{3ffe9f73-2f15-492c-9049-3ec5a11c13e5}"/> <edge from="{8486ca8d-6418-427f-b3cd-9dc4cc8b8308}" id="{af8ae996-ffef-4c28-ab35-7bad88460ad0}" partId="{b8b3ef86-c3fb-413e-81b2-22704d2c9bde}" to="{3ffe9f73-2f15-492c-9049-3ec5a11c13e5}"/>
<edge from="{d865109f-5575-4663-8d0b-e41776fd0c9a}" id="{b1572a57-b371-454a-a90a-bc875fb4ade2}" partId="{bd1b2d09-62da-44c7-a0d0-d37f9943eaa7}" to="{768d8ac5-7c00-4faf-9a4f-13993f69213a}"/> <edge from="{d865109f-5575-4663-8d0b-e41776fd0c9a}" id="{b1572a57-b371-454a-a90a-bc875fb4ade2}" partId="{62d7ef5f-8f47-4aeb-bb39-97da75e181ea}" to="{768d8ac5-7c00-4faf-9a4f-13993f69213a}"/>
<edge from="{c1bfe566-ede8-451c-aff5-67440421761f}" id="{c33aa039-0678-43a3-94c5-b4f318c1207c}" partId="{92f4278b-354e-45fb-83ea-6464be1d3309}" to="{577919f7-ebd4-4d95-954a-f1e53757bf7c}"/> <edge from="{c1bfe566-ede8-451c-aff5-67440421761f}" id="{c33aa039-0678-43a3-94c5-b4f318c1207c}" partId="{a5feecf2-e126-4034-8cde-597ad5d0e412}" to="{577919f7-ebd4-4d95-954a-f1e53757bf7c}"/>
<edge from="{72e92d06-1c01-43cb-8de3-ec6f9a74301d}" id="{cf52057c-e1fb-4697-bae6-97edf29d054a}" partId="{c30b83af-faa5-4697-88f4-145eccc4a141}" to="{002d2643-ad37-4b31-9427-ffa80d75cef0}"/> <edge from="{72e92d06-1c01-43cb-8de3-ec6f9a74301d}" id="{cf52057c-e1fb-4697-bae6-97edf29d054a}" partId="{b8b3ef86-c3fb-413e-81b2-22704d2c9bde}" to="{002d2643-ad37-4b31-9427-ffa80d75cef0}"/>
<edge from="{c1ef7958-3e56-40cb-88b2-5c3baaa8dda9}" id="{d06fec43-319e-4a6c-85da-2350a49c5f1f}" partId="{78b71f39-5010-4771-8d90-aaa08f38aa7a}" to="{c95cfb16-56f5-4e55-b96f-685ef3735e33}"/> <edge from="{c1ef7958-3e56-40cb-88b2-5c3baaa8dda9}" id="{d06fec43-319e-4a6c-85da-2350a49c5f1f}" partId="{f5133a44-dda9-4189-bba6-7d291eb5453e}" to="{c95cfb16-56f5-4e55-b96f-685ef3735e33}"/>
<edge from="{2be2d852-dc7e-4082-b3bd-fb7c7b67cd9e}" id="{ecf79230-c93d-459d-886f-b42bf0947edf}" partId="{c30b83af-faa5-4697-88f4-145eccc4a141}" to="{9f38a9ee-e7e5-4e63-8d55-0623ca9ef5f2}"/> <edge from="{2be2d852-dc7e-4082-b3bd-fb7c7b67cd9e}" id="{ecf79230-c93d-459d-886f-b42bf0947edf}" partId="{b8b3ef86-c3fb-413e-81b2-22704d2c9bde}" to="{9f38a9ee-e7e5-4e63-8d55-0623ca9ef5f2}"/>
<edge from="{0773edbe-d60e-4df9-ac1a-b228f69aeb9e}" id="{f5924de5-ebc7-4fa3-8a0c-b48300e75d3f}" partId="{af705abc-552c-43b7-9518-5c299bbbaefe}" to="{0c6e2b32-45d5-4c33-8f41-f8d259f3bfaf}"/> <edge from="{0773edbe-d60e-4df9-ac1a-b228f69aeb9e}" id="{f5924de5-ebc7-4fa3-8a0c-b48300e75d3f}" partId="{1933a268-77e5-4c9d-9a0e-81adb51be9cf}" to="{0c6e2b32-45d5-4c33-8f41-f8d259f3bfaf}"/>
<edge from="{ec6f812c-2def-4efc-be46-f00e8ea5d53c}" id="{f83edf96-acbd-4f4a-8355-765439eef106}" partId="{a0fd6b1b-efc3-4211-a439-4e87d2fdb3f9}" to="{105c0e26-729b-4f41-9c57-5cc6a8feb5c4}"/> <edge from="{ec6f812c-2def-4efc-be46-f00e8ea5d53c}" id="{f83edf96-acbd-4f4a-8355-765439eef106}" partId="{ae61b00a-6fa7-4d27-9b78-43f87bac6823}" to="{105c0e26-729b-4f41-9c57-5cc6a8feb5c4}"/>
</edges> </edges>
<parts> <parts>
<part chamfered="false" color="#ff000008" disabled="false" id="{60038150-9349-422c-96b1-cead9d97ab73}" locked="false" rounded="true" subdived="false" visible="true" xMirrored="true"/> <part chamfered="false" color="#ff100f0a" disabled="false" id="{1933a268-77e5-4c9d-9a0e-81adb51be9cf}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="true"/>
<part base="YZ" chamfered="false" color="#ff918d8a" colorSolubility="0.02" cutFace="{bd1b2d09-62da-44c7-a0d0-d37f9943eaa7}" cutRotation="-0.62" disabled="false" id="{78b71f39-5010-4771-8d90-aaa08f38aa7a}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="true"/> <part chamfered="false" color="#ff000008" disabled="false" id="{52600dec-a5db-4d32-b175-fef8168d9f69}" locked="false" rounded="true" subdived="false" visible="true" xMirrored="true"/>
<part chamfered="false" color="#ff7a160c" disabled="false" id="{92f4278b-354e-45fb-83ea-6464be1d3309}" locked="false" rounded="true" subdived="false" visible="true" xMirrored="false"/> <part chamfered="false" disabled="false" id="{62d7ef5f-8f47-4aeb-bb39-97da75e181ea}" locked="false" rounded="false" subdived="false" target="CutFace" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ff43464a" colorSolubility="0.03" cutFace="Pentagon" deformThickness="0.67" disabled="false" id="{a0fd6b1b-efc3-4211-a439-4e87d2fdb3f9}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="false"/> <part chamfered="false" disabled="false" id="{74ab9ba2-f9d5-4d65-abb0-d6f1ae73b253}" locked="false" rounded="true" subdived="true" visible="true" xMirrored="false"/>
<part chamfered="false" disabled="false" id="{aee65af2-6917-4e38-a03d-08243b9d9e24}" locked="false" rounded="true" subdived="true" visible="true" xMirrored="false"/> <part chamfered="false" color="#ff7a160c" disabled="false" id="{a5feecf2-e126-4034-8cde-597ad5d0e412}" locked="false" rounded="true" subdived="false" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ff100f0a" disabled="false" id="{af705abc-552c-43b7-9518-5c299bbbaefe}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="true"/> <part chamfered="false" color="#ff43464a" colorSolubility="0.03" cutFace="Pentagon" deformThickness="0.67" disabled="false" id="{ae61b00a-6fa7-4d27-9b78-43f87bac6823}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="false"/>
<part chamfered="false" disabled="false" id="{bd1b2d09-62da-44c7-a0d0-d37f9943eaa7}" locked="false" rounded="false" subdived="false" target="CutFace" visible="true" xMirrored="false"/> <part chamfered="false" color="#ff918d8a" countershaded="true" disabled="false" id="{b8b3ef86-c3fb-413e-81b2-22704d2c9bde}" locked="false" rounded="true" subdived="true" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ff918d8a" countershaded="true" disabled="false" id="{c30b83af-faa5-4697-88f4-145eccc4a141}" locked="false" rounded="true" subdived="true" visible="true" xMirrored="false"/> <part base="XY" chamfered="false" color="#ff918d8a" colorSolubility="0.02" cutFace="{62d7ef5f-8f47-4aeb-bb39-97da75e181ea}" cutRotation="-0.93" disabled="false" id="{f5133a44-dda9-4189-bba6-7d291eb5453e}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="true"/>
</parts> </parts>
<components> <components>
<component combineMode="Normal" expanded="false" id="{2b6785d0-6640-46fd-9b07-fb7d7d27013f}" linkData="{c30b83af-faa5-4697-88f4-145eccc4a141}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{7d85ab06-a83f-4efd-92b0-3de6b0a01ad6}" linkData="{b8b3ef86-c3fb-413e-81b2-22704d2c9bde}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{fedc5b0b-68aa-4eaa-a91f-01c4e668e169}" linkData="{aee65af2-6917-4e38-a03d-08243b9d9e24}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{4a8ddcb5-a103-43f4-94a2-28f3ddd7d664}" linkData="{74ab9ba2-f9d5-4d65-abb0-d6f1ae73b253}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{b2796ea6-be20-4deb-9a36-1c3fe6a20853}" linkData="{92f4278b-354e-45fb-83ea-6464be1d3309}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{cb4a149d-27c2-4269-b2cd-043c1187dd3e}" linkData="{a5feecf2-e126-4034-8cde-597ad5d0e412}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{384d5cf8-3408-4b27-86ee-a549c6461ba8}" linkData="{a0fd6b1b-efc3-4211-a439-4e87d2fdb3f9}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{dce09af8-44ea-44b6-bec8-c6c35ddbb32e}" linkData="{ae61b00a-6fa7-4d27-9b78-43f87bac6823}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{ad00c8b1-05cc-471f-b961-d9a3b62edec7}" linkData="{bd1b2d09-62da-44c7-a0d0-d37f9943eaa7}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{7928aa06-f34a-4b07-b3ae-163653b94ecf}" linkData="{62d7ef5f-8f47-4aeb-bb39-97da75e181ea}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{d44a51c9-001b-4b99-8dc8-c32a3206a83c}" linkData="{78b71f39-5010-4771-8d90-aaa08f38aa7a}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{934eafe9-c13b-467c-96b8-0d85556c3ba0}" linkData="{f5133a44-dda9-4189-bba6-7d291eb5453e}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{a8ca8ad7-1236-496f-a227-a07443f2af29}" linkData="{af705abc-552c-43b7-9518-5c299bbbaefe}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{086e12ff-5a8f-47ea-bed1-78f211451455}" linkData="{1933a268-77e5-4c9d-9a0e-81adb51be9cf}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{7a321445-6552-4cf0-a2aa-95295c41d313}" linkData="{60038150-9349-422c-96b1-cead9d97ab73}" linkDataType="partId"/> <component combineMode="Normal" expanded="false" id="{f3048869-1026-447a-ab9f-c16e9dc62762}" linkData="{52600dec-a5db-4d32-b175-fef8168d9f69}" linkDataType="partId"/>
</components> </components>
<materials/> <materials/>
<poses> <poses>
<pose id="{3e2fcabd-34a5-4054-8be9-f221b8282241}" name="up" yTranslationScale="0.1"> <pose id="{8463a15e-a207-448f-b899-86fa889d0dca}" name="up" yTranslationScale="0.1">
<frames> <frames>
<frame duration="1"> <frame duration="1">
<parameters> <parameters>
@ -112,7 +112,7 @@ DUST3D 1.0 xml 0000000194
</frame> </frame>
</frames> </frames>
</pose> </pose>
<pose id="{123dce5f-9a65-4baa-b7d9-92a7417c7167}" name="down" yTranslationScale="0.1"> <pose id="{23e36552-3716-4765-90a6-58d28e9038e1}" name="down" yTranslationScale="0.1">
<frames> <frames>
<frame duration="1"> <frame duration="1">
<parameters> <parameters>
@ -130,13 +130,13 @@ DUST3D 1.0 xml 0000000194
</pose> </pose>
</poses> </poses>
<motions> <motions>
<motion id="{e8da45ff-1bef-4458-a2b8-f5c3fcea8406}" name="fly"> <motion id="{32934db3-6cb9-4167-9db5-8f8953d58026}" name="fly">
<clips> <clips>
<clip duration="0" linkData="{3e2fcabd-34a5-4054-8be9-f221b8282241}" linkDataType="poseId"/> <clip duration="0" linkData="{8463a15e-a207-448f-b899-86fa889d0dca}" linkDataType="poseId"/>
<clip duration="0.3" linkData="EaseInBack" linkDataType="InterpolationType"/> <clip duration="0.3" linkData="EaseInBack" linkDataType="InterpolationType"/>
<clip duration="0" linkData="{123dce5f-9a65-4baa-b7d9-92a7417c7167}" linkDataType="poseId"/> <clip duration="0" linkData="{23e36552-3716-4765-90a6-58d28e9038e1}" linkDataType="poseId"/>
<clip duration="0.5" linkData="EaseInBack" linkDataType="InterpolationType"/> <clip duration="0.5" linkData="EaseInBack" linkDataType="InterpolationType"/>
<clip duration="0" linkData="{3e2fcabd-34a5-4054-8be9-f221b8282241}" linkDataType="poseId"/> <clip duration="0" linkData="{8463a15e-a207-448f-b899-86fa889d0dca}" linkDataType="poseId"/>
</clips> </clips>
</motion> </motion>
</motions> </motions>

View File

@ -10,7 +10,7 @@ void AnimationClipPlayer::setSpeedMode(SpeedMode speedMode)
m_speedMode = speedMode; m_speedMode = speedMode;
} }
void AnimationClipPlayer::updateFrameMeshes(std::vector<std::pair<float, MeshLoader *>> &frameMeshes) void AnimationClipPlayer::updateFrameMeshes(std::vector<std::pair<float, Model *>> &frameMeshes)
{ {
clear(); clear();
@ -50,26 +50,26 @@ int AnimationClipPlayer::getFrameDurationMillis(int frame)
return millis; return millis;
} }
MeshLoader *AnimationClipPlayer::takeFrameMesh() Model *AnimationClipPlayer::takeFrameMesh()
{ {
if (m_currentPlayIndex >= (int)m_frameMeshes.size()) { if (m_currentPlayIndex >= (int)m_frameMeshes.size()) {
if (nullptr != m_lastFrameMesh) if (nullptr != m_lastFrameMesh)
return new MeshLoader(*m_lastFrameMesh); return new Model(*m_lastFrameMesh);
return nullptr; return nullptr;
} }
int millis = getFrameDurationMillis(m_currentPlayIndex) - m_countForFrame.elapsed(); int millis = getFrameDurationMillis(m_currentPlayIndex) - m_countForFrame.elapsed();
if (millis > 0) { if (millis > 0) {
m_timerForFrame.singleShot(millis, this, &AnimationClipPlayer::frameReadyToShow); m_timerForFrame.singleShot(millis, this, &AnimationClipPlayer::frameReadyToShow);
if (nullptr != m_lastFrameMesh) if (nullptr != m_lastFrameMesh)
return new MeshLoader(*m_lastFrameMesh); return new Model(*m_lastFrameMesh);
return nullptr; return nullptr;
} }
m_currentPlayIndex = (m_currentPlayIndex + 1) % m_frameMeshes.size(); m_currentPlayIndex = (m_currentPlayIndex + 1) % m_frameMeshes.size();
m_countForFrame.restart(); m_countForFrame.restart();
MeshLoader *mesh = new MeshLoader(*m_frameMeshes[m_currentPlayIndex].second); Model *mesh = new Model(*m_frameMeshes[m_currentPlayIndex].second);
m_timerForFrame.singleShot(getFrameDurationMillis(m_currentPlayIndex), this, &AnimationClipPlayer::frameReadyToShow); m_timerForFrame.singleShot(getFrameDurationMillis(m_currentPlayIndex), this, &AnimationClipPlayer::frameReadyToShow);
delete m_lastFrameMesh; delete m_lastFrameMesh;
m_lastFrameMesh = new MeshLoader(*mesh); m_lastFrameMesh = new Model(*mesh);
return mesh; return mesh;
} }

View File

@ -3,7 +3,7 @@
#include <QObject> #include <QObject>
#include <QTimer> #include <QTimer>
#include <QTime> #include <QTime>
#include "meshloader.h" #include "model.h"
class AnimationClipPlayer : public QObject class AnimationClipPlayer : public QObject
{ {
@ -21,8 +21,8 @@ public:
}; };
~AnimationClipPlayer(); ~AnimationClipPlayer();
MeshLoader *takeFrameMesh(); Model *takeFrameMesh();
void updateFrameMeshes(std::vector<std::pair<float, MeshLoader *>> &frameMeshes); void updateFrameMeshes(std::vector<std::pair<float, Model *>> &frameMeshes);
void clear(); void clear();
public slots: public slots:
@ -32,9 +32,9 @@ private:
void freeFrames(); void freeFrames();
int getFrameDurationMillis(int frame); int getFrameDurationMillis(int frame);
MeshLoader *m_lastFrameMesh = nullptr; Model *m_lastFrameMesh = nullptr;
int m_currentPlayIndex = 0; int m_currentPlayIndex = 0;
std::vector<std::pair<float, MeshLoader *>> m_frameMeshes; std::vector<std::pair<float, Model *>> m_frameMeshes;
QTime m_countForFrame; QTime m_countForFrame;
QTimer m_timerForFrame; QTimer m_timerForFrame;
SpeedMode m_speedMode = SpeedMode::Normal; SpeedMode m_speedMode = SpeedMode::Normal;

View File

@ -1,7 +1,7 @@
#include <QVector2D> #include <QVector2D>
#include <iostream> #include <iostream>
#include "boundingboxmesh.h" #include "boundingboxmesh.h"
#include "meshloader.h" #include "model.h"
#include "util.h" #include "util.h"
ShaderVertex *buildBoundingBoxMeshEdges(const std::vector<std::tuple<QVector3D, QVector3D, float, float, QColor>> &boxes, ShaderVertex *buildBoundingBoxMeshEdges(const std::vector<std::tuple<QVector3D, QVector3D, float, float, QColor>> &boxes,
@ -73,8 +73,8 @@ ShaderVertex *buildBoundingBoxMeshEdges(const std::vector<std::tuple<QVector3D,
currentVertex.normX = 0; currentVertex.normX = 0;
currentVertex.normY = 1; currentVertex.normY = 1;
currentVertex.normZ = 0; currentVertex.normZ = 0;
currentVertex.metalness = MeshLoader::m_defaultMetalness; currentVertex.metalness = Model::m_defaultMetalness;
currentVertex.roughness = MeshLoader::m_defaultRoughness; currentVertex.roughness = Model::m_defaultRoughness;
currentVertex.tangentX = 0; currentVertex.tangentX = 0;
currentVertex.tangentY = 0; currentVertex.tangentY = 0;
currentVertex.tangentZ = 0; currentVertex.tangentZ = 0;
@ -93,9 +93,9 @@ ShaderVertex *buildBoundingBoxMeshEdges(const std::vector<std::tuple<QVector3D,
return edgeVertices; return edgeVertices;
} }
MeshLoader *buildBoundingBoxMesh(const std::vector<std::tuple<QVector3D, QVector3D, float, float, QColor>> &boxes) Model *buildBoundingBoxMesh(const std::vector<std::tuple<QVector3D, QVector3D, float, float, QColor>> &boxes)
{ {
int edgeVerticesNum = 0; int edgeVerticesNum = 0;
ShaderVertex *edgeVertices = buildBoundingBoxMeshEdges(boxes, &edgeVerticesNum); ShaderVertex *edgeVertices = buildBoundingBoxMeshEdges(boxes, &edgeVerticesNum);
return new MeshLoader(nullptr, 0, edgeVertices, edgeVerticesNum); return new Model(nullptr, 0, edgeVertices, edgeVerticesNum);
} }

View File

@ -4,10 +4,10 @@
#include <QVector3D> #include <QVector3D>
#include <QColor> #include <QColor>
#include "shadervertex.h" #include "shadervertex.h"
#include "meshloader.h" #include "model.h"
ShaderVertex *buildBoundingBoxMeshEdges(const std::vector<std::tuple<QVector3D, QVector3D, float, float, QColor>> &boxes, ShaderVertex *buildBoundingBoxMeshEdges(const std::vector<std::tuple<QVector3D, QVector3D, float, float, QColor>> &boxes,
int *edgeVerticesNum); int *edgeVerticesNum);
MeshLoader *buildBoundingBoxMesh(const std::vector<std::tuple<QVector3D, QVector3D, float, float, QColor>> &boxes); Model *buildBoundingBoxMesh(const std::vector<std::tuple<QVector3D, QVector3D, float, float, QColor>> &boxes);
#endif #endif

View File

@ -4,6 +4,7 @@
#include <QUuid> #include <QUuid>
#include <cmath> #include <cmath>
#include <QTransform> #include <QTransform>
#include <QGuiApplication>
#include "contourtopartconverter.h" #include "contourtopartconverter.h"
#include "imageskeletonextractor.h" #include "imageskeletonextractor.h"
#include "util.h" #include "util.h"
@ -23,6 +24,7 @@ ContourToPartConverter::ContourToPartConverter(const QPolygonF &mainProfile,
void ContourToPartConverter::process() void ContourToPartConverter::process()
{ {
convert(); convert();
this->moveToThread(QGuiApplication::instance()->thread());
emit finished(); emit finished();
} }

View File

@ -45,7 +45,7 @@ void CutFaceWidget::updatePreview(QUuid partId)
qDebug() << "Part not found:" << m_partId; qDebug() << "Part not found:" << m_partId;
return; return;
} }
MeshLoader *previewMesh = part->takePreviewMesh(); Model *previewMesh = part->takePreviewMesh();
m_previewWidget->updateMesh(previewMesh); m_previewWidget->updateMesh(previewMesh);
} }

View File

@ -170,6 +170,24 @@ void Document::breakEdge(QUuid edgeId)
addEdge(middleNodeId, secondNodeId); addEdge(middleNodeId, secondNodeId);
} }
void Document::reverseEdge(QUuid edgeId)
{
SkeletonEdge *edge = (SkeletonEdge *)findEdge(edgeId);
if (nullptr == edge) {
qDebug() << "Find edge failed:" << edgeId;
return;
}
if (edge->nodeIds.size() != 2) {
return;
}
std::swap(edge->nodeIds[0], edge->nodeIds[1]);
auto part = partMap.find(edge->partId);
if (part != partMap.end())
part->second.dirty = true;
emit edgeReversed(edgeId);
emit skeletonChanged();
}
void Document::removeEdge(QUuid edgeId) void Document::removeEdge(QUuid edgeId)
{ {
const SkeletonEdge *edge = findEdge(edgeId); const SkeletonEdge *edge = findEdge(edgeId);
@ -346,7 +364,7 @@ void Document::addPartByPolygons(const QPolygonF &mainProfile, const QPolygonF &
connect(contourToPartConverter, &ContourToPartConverter::finished, this, [=]() { connect(contourToPartConverter, &ContourToPartConverter::finished, this, [=]() {
const auto &snapshot = contourToPartConverter->getSnapshot(); const auto &snapshot = contourToPartConverter->getSnapshot();
if (!snapshot.nodes.empty()) { if (!snapshot.nodes.empty()) {
addFromSnapshot(snapshot, true); addFromSnapshot(snapshot, SnapshotSource::Paste);
saveSnapshot(); saveSnapshot();
} }
delete contourToPartConverter; delete contourToPartConverter;
@ -1145,6 +1163,8 @@ void Document::toSnapshot(Snapshot *snapshot, const std::set<QUuid> &limitNodeId
part["cutFace"] = CutFaceToString(partIt.second.cutFace); part["cutFace"] = CutFaceToString(partIt.second.cutFace);
} }
} }
if (!partIt.second.fillMeshLinkedId.isNull())
part["fillMesh"] = partIt.second.fillMeshLinkedId.toString();
part["dirty"] = partIt.second.dirty ? "true" : "false"; part["dirty"] = partIt.second.dirty ? "true" : "false";
if (partIt.second.hasColor) if (partIt.second.hasColor)
part["color"] = partIt.second.color.name(QColor::HexArgb); part["color"] = partIt.second.color.name(QColor::HexArgb);
@ -1443,11 +1463,11 @@ void Document::createSinglePartFromEdges(const std::vector<QVector3D> &nodes,
emit skeletonChanged(); emit skeletonChanged();
} }
void Document::addFromSnapshot(const Snapshot &snapshot, bool fromPaste) void Document::addFromSnapshot(const Snapshot &snapshot, enum SnapshotSource source)
{ {
bool isOriginChanged = false; bool isOriginChanged = false;
bool isRigTypeChanged = false; bool isRigTypeChanged = false;
if (!fromPaste) { if (SnapshotSource::Paste != source) {
this->polyCount = PolyCountFromString(valueOfKeyInMapOrEmpty(snapshot.canvas, "polyCount").toUtf8().constData()); this->polyCount = PolyCountFromString(valueOfKeyInMapOrEmpty(snapshot.canvas, "polyCount").toUtf8().constData());
const auto &originXit = snapshot.canvas.find("originX"); const auto &originXit = snapshot.canvas.find("originX");
const auto &originYit = snapshot.canvas.find("originY"); const auto &originYit = snapshot.canvas.find("originY");
@ -1482,38 +1502,41 @@ void Document::addFromSnapshot(const Snapshot &snapshot, bool fromPaste)
qDebug() << "Unsupported material type:" << materialType; qDebug() << "Unsupported material type:" << materialType;
continue; continue;
} }
QUuid newMaterialId = QUuid::createUuid(); QUuid oldMaterialId = QUuid(valueOfKeyInMapOrEmpty(materialAttributes, "id"));
auto &newMaterial = materialMap[newMaterialId]; QUuid newMaterialId = SnapshotSource::Import == source ? oldMaterialId : QUuid::createUuid();
newMaterial.id = newMaterialId; oldNewIdMap[oldMaterialId] = newMaterialId;
newMaterial.name = valueOfKeyInMapOrEmpty(materialAttributes, "name"); if (materialMap.end() == materialMap.find(newMaterialId)) {
oldNewIdMap[QUuid(valueOfKeyInMapOrEmpty(materialAttributes, "id"))] = newMaterialId; auto &newMaterial = materialMap[newMaterialId];
for (const auto &layerIt: materialIt.second) { newMaterial.id = newMaterialId;
MaterialLayer layer; newMaterial.name = valueOfKeyInMapOrEmpty(materialAttributes, "name");
auto findTileScale = layerIt.first.find("tileScale"); for (const auto &layerIt: materialIt.second) {
if (findTileScale != layerIt.first.end()) MaterialLayer layer;
layer.tileScale = findTileScale->second.toFloat(); auto findTileScale = layerIt.first.find("tileScale");
for (const auto &mapItem: layerIt.second) { if (findTileScale != layerIt.first.end())
auto textureTypeString = valueOfKeyInMapOrEmpty(mapItem, "for"); layer.tileScale = findTileScale->second.toFloat();
auto textureType = TextureTypeFromString(textureTypeString.toUtf8().constData()); for (const auto &mapItem: layerIt.second) {
if (TextureType::None == textureType) { auto textureTypeString = valueOfKeyInMapOrEmpty(mapItem, "for");
qDebug() << "Unsupported texture type:" << textureTypeString; auto textureType = TextureTypeFromString(textureTypeString.toUtf8().constData());
continue; if (TextureType::None == textureType) {
qDebug() << "Unsupported texture type:" << textureTypeString;
continue;
}
auto linkTypeString = valueOfKeyInMapOrEmpty(mapItem, "linkDataType");
if ("imageId" != linkTypeString) {
qDebug() << "Unsupported link data type:" << linkTypeString;
continue;
}
auto imageId = QUuid(valueOfKeyInMapOrEmpty(mapItem, "linkData"));
MaterialMap materialMap;
materialMap.imageId = imageId;
materialMap.forWhat = textureType;
layer.maps.push_back(materialMap);
} }
auto linkTypeString = valueOfKeyInMapOrEmpty(mapItem, "linkDataType"); newMaterial.layers.push_back(layer);
if ("imageId" != linkTypeString) {
qDebug() << "Unsupported link data type:" << linkTypeString;
continue;
}
auto imageId = QUuid(valueOfKeyInMapOrEmpty(mapItem, "linkData"));
MaterialMap materialMap;
materialMap.imageId = imageId;
materialMap.forWhat = textureType;
layer.maps.push_back(materialMap);
} }
newMaterial.layers.push_back(layer); materialIdList.push_back(newMaterialId);
emit materialAdded(newMaterialId);
} }
materialIdList.push_back(newMaterialId);
emit materialAdded(newMaterialId);
} }
std::map<QUuid, QUuid> cutFaceLinkedIdModifyMap; std::map<QUuid, QUuid> cutFaceLinkedIdModifyMap;
for (const auto &partKv: snapshot.parts) { for (const auto &partKv: snapshot.parts) {
@ -1550,6 +1573,12 @@ void Document::addFromSnapshot(const Snapshot &snapshot, bool fromPaste)
cutFaceLinkedIdModifyMap.insert({part.id, cutFaceLinkedId}); cutFaceLinkedIdModifyMap.insert({part.id, cutFaceLinkedId});
} }
} }
const auto &fillMeshIt = partKv.second.find("fillMesh");
if (fillMeshIt != partKv.second.end()) {
QUuid fillMeshLinkedId = QUuid(fillMeshIt->second);
if (!fillMeshLinkedId.isNull())
part.fillMeshLinkedId = fillMeshLinkedId;
}
if (isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "inverse"))) if (isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "inverse")))
inversePartIds.insert(part.id); inversePartIds.insert(part.id);
const auto &colorIt = partKv.second.find("color"); const auto &colorIt = partKv.second.find("color");
@ -1863,15 +1892,15 @@ void Document::resetScript()
void Document::fromSnapshot(const Snapshot &snapshot) void Document::fromSnapshot(const Snapshot &snapshot)
{ {
reset(); reset();
addFromSnapshot(snapshot, false); addFromSnapshot(snapshot, SnapshotSource::Unknown);
emit uncheckAll(); emit uncheckAll();
} }
MeshLoader *Document::takeResultMesh() Model *Document::takeResultMesh()
{ {
if (nullptr == m_resultMesh) if (nullptr == m_resultMesh)
return nullptr; return nullptr;
MeshLoader *resultMesh = new MeshLoader(*m_resultMesh); Model *resultMesh = new Model(*m_resultMesh);
return resultMesh; return resultMesh;
} }
@ -1880,32 +1909,32 @@ bool Document::isMeshGenerationSucceed()
return m_isMeshGenerationSucceed; return m_isMeshGenerationSucceed;
} }
MeshLoader *Document::takeResultTextureMesh() Model *Document::takeResultTextureMesh()
{ {
if (nullptr == m_resultTextureMesh) if (nullptr == m_resultTextureMesh)
return nullptr; return nullptr;
MeshLoader *resultTextureMesh = new MeshLoader(*m_resultTextureMesh); Model *resultTextureMesh = new Model(*m_resultTextureMesh);
return resultTextureMesh; return resultTextureMesh;
} }
MeshLoader *Document::takeResultRigWeightMesh() Model *Document::takeResultRigWeightMesh()
{ {
if (nullptr == m_resultRigWeightMesh) if (nullptr == m_resultRigWeightMesh)
return nullptr; return nullptr;
MeshLoader *resultMesh = new MeshLoader(*m_resultRigWeightMesh); Model *resultMesh = new Model(*m_resultRigWeightMesh);
return resultMesh; return resultMesh;
} }
void Document::meshReady() void Document::meshReady()
{ {
MeshLoader *resultMesh = m_meshGenerator->takeResultMesh(); Model *resultMesh = m_meshGenerator->takeResultMesh();
Outcome *outcome = m_meshGenerator->takeOutcome(); Outcome *outcome = m_meshGenerator->takeOutcome();
bool isSucceed = m_meshGenerator->isSucceed(); bool isSuccessful = m_meshGenerator->isSuccessful();
for (auto &partId: m_meshGenerator->generatedPreviewPartIds()) { for (auto &partId: m_meshGenerator->generatedPreviewPartIds()) {
auto part = partMap.find(partId); auto part = partMap.find(partId);
if (part != partMap.end()) { if (part != partMap.end()) {
MeshLoader *resultPartPreviewMesh = m_meshGenerator->takePartPreviewMesh(partId); Model *resultPartPreviewMesh = m_meshGenerator->takePartPreviewMesh(partId);
part->second.updatePreviewMesh(resultPartPreviewMesh); part->second.updatePreviewMesh(resultPartPreviewMesh);
emit partPreviewChanged(partId); emit partPreviewChanged(partId);
} }
@ -1922,7 +1951,7 @@ void Document::meshReady()
//addToolToMesh(m_resultMesh); //addToolToMesh(m_resultMesh);
m_isMeshGenerationSucceed = isSucceed; m_isMeshGenerationSucceed = isSuccessful;
delete m_currentOutcome; delete m_currentOutcome;
m_currentOutcome = outcome; m_currentOutcome = outcome;
@ -1946,7 +1975,7 @@ void Document::meshReady()
} }
} }
//void Document::addToolToMesh(MeshLoader *mesh) //void Document::addToolToMesh(Model *mesh)
//{ //{
// if (nullptr == mesh) // if (nullptr == mesh)
// return; // return;
@ -2136,7 +2165,7 @@ void Document::postProcess()
m_isPostProcessResultObsolete = false; m_isPostProcessResultObsolete = false;
if (!m_currentOutcome) { if (!m_currentOutcome) {
qDebug() << "MeshLoader is null"; qDebug() << "Model is null";
return; return;
} }
@ -2188,7 +2217,7 @@ void Document::doPickMouseTarget()
m_isMouseTargetResultObsolete = false; m_isMouseTargetResultObsolete = false;
if (!m_currentOutcome) { if (!m_currentOutcome) {
qDebug() << "MeshLoader is null"; qDebug() << "Model is null";
return; return;
} }
@ -3179,7 +3208,7 @@ void Document::paste()
QXmlStreamReader xmlStreamReader(mimeData->text()); QXmlStreamReader xmlStreamReader(mimeData->text());
Snapshot snapshot; Snapshot snapshot;
loadSkeletonFromXmlStream(&snapshot, xmlStreamReader); loadSkeletonFromXmlStream(&snapshot, xmlStreamReader);
addFromSnapshot(snapshot, true); addFromSnapshot(snapshot, SnapshotSource::Paste);
} }
} }
@ -3496,7 +3525,7 @@ void Document::generateRig()
void Document::rigReady() void Document::rigReady()
{ {
m_currentRigSucceed = m_rigGenerator->isSucceed(); m_currentRigSucceed = m_rigGenerator->isSuccessful();
delete m_resultRigWeightMesh; delete m_resultRigWeightMesh;
m_resultRigWeightMesh = m_rigGenerator->takeResultMesh(); m_resultRigWeightMesh = m_rigGenerator->takeResultMesh();
@ -3701,7 +3730,7 @@ void Document::posePreviewsReady()
for (const auto &poseIdAndFrame: m_posePreviewsGenerator->generatedPreviewPoseIdAndFrames()) { for (const auto &poseIdAndFrame: m_posePreviewsGenerator->generatedPreviewPoseIdAndFrames()) {
auto pose = poseMap.find(poseIdAndFrame.first); auto pose = poseMap.find(poseIdAndFrame.first);
if (pose != poseMap.end()) { if (pose != poseMap.end()) {
MeshLoader *resultPartPreviewMesh = m_posePreviewsGenerator->takePreview(poseIdAndFrame); Model *resultPartPreviewMesh = m_posePreviewsGenerator->takePreview(poseIdAndFrame);
pose->second.updatePreviewMesh(resultPartPreviewMesh); pose->second.updatePreviewMesh(resultPartPreviewMesh);
emit posePreviewChanged(poseIdAndFrame.first); emit posePreviewChanged(poseIdAndFrame.first);
} }
@ -3717,6 +3746,12 @@ void Document::posePreviewsReady()
void Document::addMaterial(QUuid materialId, QString name, std::vector<MaterialLayer> layers) void Document::addMaterial(QUuid materialId, QString name, std::vector<MaterialLayer> layers)
{ {
auto findMaterialResult = materialMap.find(materialId);
if (findMaterialResult != materialMap.end()) {
qDebug() << "Material already exist:" << materialId;
return;
}
QUuid newMaterialId = materialId; QUuid newMaterialId = materialId;
auto &material = materialMap[newMaterialId]; auto &material = materialMap[newMaterialId];
material.id = newMaterialId; material.id = newMaterialId;
@ -3815,7 +3850,7 @@ void Document::materialPreviewsReady()
for (const auto &materialId: m_materialPreviewsGenerator->generatedPreviewMaterialIds()) { for (const auto &materialId: m_materialPreviewsGenerator->generatedPreviewMaterialIds()) {
auto material = materialMap.find(materialId); auto material = materialMap.find(materialId);
if (material != materialMap.end()) { if (material != materialMap.end()) {
MeshLoader *resultPartPreviewMesh = m_materialPreviewsGenerator->takePreview(materialId); Model *resultPartPreviewMesh = m_materialPreviewsGenerator->takePreview(materialId);
material->second.updatePreviewMesh(resultPartPreviewMesh); material->second.updatePreviewMesh(resultPartPreviewMesh);
emit materialPreviewChanged(materialId); emit materialPreviewChanged(materialId);
} }

View File

@ -11,7 +11,7 @@
#include <algorithm> #include <algorithm>
#include <QPolygon> #include <QPolygon>
#include "snapshot.h" #include "snapshot.h"
#include "meshloader.h" #include "model.h"
#include "meshgenerator.h" #include "meshgenerator.h"
#include "theme.h" #include "theme.h"
#include "texturegenerator.h" #include "texturegenerator.h"
@ -231,16 +231,16 @@ public:
QUuid turnaroundImageId; QUuid turnaroundImageId;
float yTranslationScale = 1.0; float yTranslationScale = 1.0;
std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>> frames; // pair<attributes, parameters> std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>> frames; // pair<attributes, parameters>
void updatePreviewMesh(MeshLoader *previewMesh) void updatePreviewMesh(Model *previewMesh)
{ {
delete m_previewMesh; delete m_previewMesh;
m_previewMesh = previewMesh; m_previewMesh = previewMesh;
} }
MeshLoader *takePreviewMesh() const Model *takePreviewMesh() const
{ {
if (nullptr == m_previewMesh) if (nullptr == m_previewMesh)
return nullptr; return nullptr;
return new MeshLoader(*m_previewMesh); return new Model(*m_previewMesh);
} }
bool yTranslationScaleAdjusted() const bool yTranslationScaleAdjusted() const
{ {
@ -248,7 +248,7 @@ public:
} }
private: private:
Q_DISABLE_COPY(Pose); Q_DISABLE_COPY(Pose);
MeshLoader *m_previewMesh = nullptr; Model *m_previewMesh = nullptr;
}; };
enum class MotionClipType enum class MotionClipType
@ -327,18 +327,18 @@ public:
bool dirty = true; bool dirty = true;
std::vector<MotionClip> clips; std::vector<MotionClip> clips;
std::vector<std::pair<float, JointNodeTree>> jointNodeTrees; std::vector<std::pair<float, JointNodeTree>> jointNodeTrees;
void updatePreviewMeshs(std::vector<std::pair<float, MeshLoader *>> &previewMeshs) void updatePreviewMeshs(std::vector<std::pair<float, Model *>> &previewMeshs)
{ {
releasePreviewMeshs(); releasePreviewMeshs();
m_previewMeshs = previewMeshs; m_previewMeshs = previewMeshs;
previewMeshs.clear(); previewMeshs.clear();
} }
MeshLoader *takePreviewMesh() const Model *takePreviewMesh() const
{ {
if (m_previewMeshs.empty()) if (m_previewMeshs.empty())
return nullptr; return nullptr;
int middle = std::max((int)m_previewMeshs.size() / 2 - 1, (int)0); int middle = std::max((int)m_previewMeshs.size() / 2 - 1, (int)0);
return new MeshLoader(*m_previewMeshs[middle].second); return new Model(*m_previewMeshs[middle].second);
} }
private: private:
Q_DISABLE_COPY(Motion); Q_DISABLE_COPY(Motion);
@ -349,7 +349,7 @@ private:
} }
m_previewMeshs.clear(); m_previewMeshs.clear();
} }
std::vector<std::pair<float, MeshLoader *>> m_previewMeshs; std::vector<std::pair<float, Model *>> m_previewMeshs;
}; };
class MaterialMap class MaterialMap
@ -380,20 +380,20 @@ public:
QString name; QString name;
bool dirty = true; bool dirty = true;
std::vector<MaterialLayer> layers; std::vector<MaterialLayer> layers;
void updatePreviewMesh(MeshLoader *previewMesh) void updatePreviewMesh(Model *previewMesh)
{ {
delete m_previewMesh; delete m_previewMesh;
m_previewMesh = previewMesh; m_previewMesh = previewMesh;
} }
MeshLoader *takePreviewMesh() const Model *takePreviewMesh() const
{ {
if (nullptr == m_previewMesh) if (nullptr == m_previewMesh)
return nullptr; return nullptr;
return new MeshLoader(*m_previewMesh); return new Model(*m_previewMesh);
} }
private: private:
Q_DISABLE_COPY(Material); Q_DISABLE_COPY(Material);
MeshLoader *m_previewMesh = nullptr; Model *m_previewMesh = nullptr;
}; };
enum class DocumentToSnapshotFor enum class DocumentToSnapshotFor
@ -434,7 +434,7 @@ signals:
void nodeCutRotationChanged(QUuid nodeId); void nodeCutRotationChanged(QUuid nodeId);
void nodeCutFaceChanged(QUuid nodeId); void nodeCutFaceChanged(QUuid nodeId);
void nodeOriginChanged(QUuid nodeId); void nodeOriginChanged(QUuid nodeId);
void edgeChanged(QUuid edgeId); void edgeReversed(QUuid edgeId);
void partPreviewChanged(QUuid partId); void partPreviewChanged(QUuid partId);
void resultMeshChanged(); void resultMeshChanged();
void turnaroundChanged(); void turnaroundChanged();
@ -562,17 +562,23 @@ public:
const std::set<QUuid> &limitMotionIds=std::set<QUuid>(), const std::set<QUuid> &limitMotionIds=std::set<QUuid>(),
const std::set<QUuid> &limitMaterialIds=std::set<QUuid>()) const; const std::set<QUuid> &limitMaterialIds=std::set<QUuid>()) const;
void fromSnapshot(const Snapshot &snapshot); void fromSnapshot(const Snapshot &snapshot);
void addFromSnapshot(const Snapshot &snapshot, bool fromPaste=true); enum class SnapshotSource
{
Unknown,
Paste,
Import
};
void addFromSnapshot(const Snapshot &snapshot, enum SnapshotSource source=SnapshotSource::Paste);
const Component *findComponent(QUuid componentId) const; const Component *findComponent(QUuid componentId) const;
const Component *findComponentParent(QUuid componentId) const; const Component *findComponentParent(QUuid componentId) const;
QUuid findComponentParentId(QUuid componentId) const; QUuid findComponentParentId(QUuid componentId) const;
const Material *findMaterial(QUuid materialId) const; const Material *findMaterial(QUuid materialId) const;
const Pose *findPose(QUuid poseId) const; const Pose *findPose(QUuid poseId) const;
const Motion *findMotion(QUuid motionId) const; const Motion *findMotion(QUuid motionId) const;
MeshLoader *takeResultMesh(); Model *takeResultMesh();
bool isMeshGenerationSucceed(); bool isMeshGenerationSucceed();
MeshLoader *takeResultTextureMesh(); Model *takeResultTextureMesh();
MeshLoader *takeResultRigWeightMesh(); Model *takeResultRigWeightMesh();
const std::vector<RiggerBone> *resultRigBones() const; const std::vector<RiggerBone> *resultRigBones() const;
const std::map<int, RiggerVertexWeights> *resultRigWeights() const; const std::map<int, RiggerVertexWeights> *resultRigWeights() const;
void updateTurnaround(const QImage &image); void updateTurnaround(const QImage &image);
@ -707,6 +713,7 @@ public slots:
void silentReset(); void silentReset();
void silentResetScript(); void silentResetScript();
void breakEdge(QUuid edgeId); void breakEdge(QUuid edgeId);
void reverseEdge(QUuid edgeId);
void setXlockState(bool locked); void setXlockState(bool locked);
void setYlockState(bool locked); void setYlockState(bool locked);
void setZlockState(bool locked); void setZlockState(bool locked);
@ -760,13 +767,13 @@ private:
void markAllDirty(); void markAllDirty();
void removeRigResults(); void removeRigResults();
void updateLinkedPart(QUuid oldPartId, QUuid newPartId); void updateLinkedPart(QUuid oldPartId, QUuid newPartId);
//void addToolToMesh(MeshLoader *mesh); //void addToolToMesh(Model *mesh);
bool updateDefaultVariables(const std::map<QString, std::map<QString, QString>> &defaultVariables); bool updateDefaultVariables(const std::map<QString, std::map<QString, QString>> &defaultVariables);
void checkPartGrid(QUuid partId); void checkPartGrid(QUuid partId);
private: // need initialize private: // need initialize
bool m_isResultMeshObsolete; bool m_isResultMeshObsolete;
MeshGenerator *m_meshGenerator; MeshGenerator *m_meshGenerator;
MeshLoader *m_resultMesh; Model *m_resultMesh;
std::map<QUuid, StrokeMeshBuilder::CutFaceTransform> *m_resultMeshCutFaceTransforms; std::map<QUuid, StrokeMeshBuilder::CutFaceTransform> *m_resultMeshCutFaceTransforms;
std::map<QUuid, std::map<QString, QVector2D>> *m_resultMeshNodesCutFaces; std::map<QUuid, std::map<QString, QVector2D>> *m_resultMeshNodesCutFaces;
bool m_isMeshGenerationSucceed; bool m_isMeshGenerationSucceed;
@ -777,13 +784,13 @@ private: // need initialize
bool m_isPostProcessResultObsolete; bool m_isPostProcessResultObsolete;
MeshResultPostProcessor *m_postProcessor; MeshResultPostProcessor *m_postProcessor;
Outcome *m_postProcessedOutcome; Outcome *m_postProcessedOutcome;
MeshLoader *m_resultTextureMesh; Model *m_resultTextureMesh;
unsigned long long m_textureImageUpdateVersion; unsigned long long m_textureImageUpdateVersion;
QUuid m_currentCanvasComponentId; QUuid m_currentCanvasComponentId;
bool m_allPositionRelatedLocksEnabled; bool m_allPositionRelatedLocksEnabled;
bool m_smoothNormal; bool m_smoothNormal;
RigGenerator *m_rigGenerator; RigGenerator *m_rigGenerator;
MeshLoader *m_resultRigWeightMesh; Model *m_resultRigWeightMesh;
std::vector<RiggerBone> *m_resultRigBones; std::vector<RiggerBone> *m_resultRigBones;
std::map<int, RiggerVertexWeights> *m_resultRigWeights; std::map<int, RiggerVertexWeights> *m_resultRigWeights;
bool m_isRigObsolete; bool m_isRigObsolete;

View File

@ -1,10 +1,12 @@
#include <QXmlStreamWriter> #include <QXmlStreamWriter>
#include <set> #include <set>
#include <QGuiApplication>
#include "documentsaver.h" #include "documentsaver.h"
#include "imageforever.h" #include "imageforever.h"
#include "ds3file.h" #include "ds3file.h"
#include "snapshotxml.h" #include "snapshotxml.h"
#include "variablesxml.h" #include "variablesxml.h"
#include "fileforever.h"
DocumentSaver::DocumentSaver(const QString *filename, DocumentSaver::DocumentSaver(const QString *filename,
Snapshot *snapshot, Snapshot *snapshot,
@ -34,6 +36,7 @@ void DocumentSaver::process()
m_turnaroundPngByteArray, m_turnaroundPngByteArray,
m_script, m_script,
m_variables); m_variables);
this->moveToThread(QGuiApplication::instance()->thread());
emit finished(); emit finished();
} }
@ -68,6 +71,7 @@ bool DocumentSaver::save(const QString *filename,
} }
std::set<QUuid> imageIds; std::set<QUuid> imageIds;
std::set<QUuid> fileIds;
for (const auto &material: snapshot->materials) { for (const auto &material: snapshot->materials) {
for (auto &layer: material.second) { for (auto &layer: material.second) {
@ -80,6 +84,7 @@ bool DocumentSaver::save(const QString *filename,
} }
} }
} }
for (const auto &part: snapshot->parts) { for (const auto &part: snapshot->parts) {
auto findImageIdString = part.second.find("deformMapImageId"); auto findImageIdString = part.second.find("deformMapImageId");
if (findImageIdString == part.second.end()) if (findImageIdString == part.second.end())
@ -88,6 +93,16 @@ bool DocumentSaver::save(const QString *filename,
imageIds.insert(imageId); imageIds.insert(imageId);
} }
for (const auto &part: snapshot->parts) {
auto fillMeshLinkedIdString = part.second.find("fillMesh");
if (fillMeshLinkedIdString == part.second.end())
continue;
QUuid fileId = QUuid(fillMeshLinkedIdString->second);
if (fileId.isNull())
continue;
fileIds.insert(fileId);
}
for (auto &pose: snapshot->poses) { for (auto &pose: snapshot->poses) {
auto findCanvasImageId = pose.first.find("canvasImageId"); auto findCanvasImageId = pose.first.find("canvasImageId");
if (findCanvasImageId != pose.first.end()) { if (findCanvasImageId != pose.first.end()) {
@ -104,5 +119,20 @@ bool DocumentSaver::save(const QString *filename,
ds3Writer.add("images/" + imageId.toString() + ".png", "asset", pngByteArray); ds3Writer.add("images/" + imageId.toString() + ".png", "asset", pngByteArray);
} }
for (const auto &fileId: fileIds) {
const QByteArray *byteArray = FileForever::getContent(fileId);
if (nullptr == byteArray)
continue;
QString suffix = ".bin";
const QString *name = FileForever::getName(fileId);
if (nullptr != name) {
int suffixBegin = name->lastIndexOf(".");
if (-1 != suffixBegin)
suffix = name->mid(suffixBegin);
}
if (byteArray->size() > 0)
ds3Writer.add("files/" + fileId.toString() + suffix, "asset", byteArray);
}
return ds3Writer.save(*filename); return ds3Writer.save(*filename);
} }

View File

@ -16,6 +16,11 @@ public:
QString *script, QString *script,
std::map<QString, std::map<QString, QString>> *variables); std::map<QString, std::map<QString, QString>> *variables);
~DocumentSaver(); ~DocumentSaver();
static bool save(const QString *filename,
Snapshot *snapshot,
const QByteArray *turnaroundPngByteArray,
const QString *script,
const std::map<QString, std::map<QString, QString>> *variables);
signals: signals:
void finished(); void finished();
public slots: public slots:
@ -26,12 +31,6 @@ private:
QByteArray *m_turnaroundPngByteArray = nullptr; QByteArray *m_turnaroundPngByteArray = nullptr;
QString *m_script = nullptr; QString *m_script = nullptr;
std::map<QString, std::map<QString, QString>> *m_variables = nullptr; std::map<QString, std::map<QString, QString>> *m_variables = nullptr;
static bool save(const QString *filename,
Snapshot *snapshot,
const QByteArray *turnaroundPngByteArray,
const QString *script,
const std::map<QString, std::map<QString, QString>> *variables);
}; };
#endif #endif

View File

@ -45,7 +45,9 @@
#include "scriptwidget.h" #include "scriptwidget.h"
#include "variablesxml.h" #include "variablesxml.h"
#include "updatescheckwidget.h" #include "updatescheckwidget.h"
#include "modelofflinerender.h" #include "modeloffscreenrender.h"
#include "fileforever.h"
#include "documentsaver.h"
int DocumentWindow::m_modelRenderWidgetInitialX = 16; int DocumentWindow::m_modelRenderWidgetInitialX = 16;
int DocumentWindow::m_modelRenderWidgetInitialY = 16; int DocumentWindow::m_modelRenderWidgetInitialY = 16;
@ -233,12 +235,12 @@ DocumentWindow::DocumentWindow() :
//rotateClockwiseButton->setToolTip(tr("Rotate whole model")); //rotateClockwiseButton->setToolTip(tr("Rotate whole model"));
//Theme::initAwesomeButton(rotateClockwiseButton); //Theme::initAwesomeButton(rotateClockwiseButton);
auto updateRegenerateIconAndTips = [&](SpinnableAwesomeButton *regenerateButton, bool isSucceed, bool forceUpdate=false) { auto updateRegenerateIconAndTips = [&](SpinnableAwesomeButton *regenerateButton, bool isSuccessful, bool forceUpdate=false) {
if (!forceUpdate) { if (!forceUpdate) {
if (m_isLastMeshGenerationSucceed == isSucceed) if (m_isLastMeshGenerationSucceed == isSuccessful)
return; return;
} }
m_isLastMeshGenerationSucceed = isSucceed; m_isLastMeshGenerationSucceed = isSuccessful;
regenerateButton->setToolTip(m_isLastMeshGenerationSucceed ? tr("Regenerate") : tr("Mesh generation failed, please undo or adjust recent changed nodes\nTips:\n - Don't let generated mesh self-intersect\n - Make multiple parts instead of one single part for whole model")); regenerateButton->setToolTip(m_isLastMeshGenerationSucceed ? tr("Regenerate") : tr("Mesh generation failed, please undo or adjust recent changed nodes\nTips:\n - Don't let generated mesh self-intersect\n - Make multiple parts instead of one single part for whole model"));
regenerateButton->setAwesomeIcon(m_isLastMeshGenerationSucceed ? QChar(fa::recycle) : QChar(fa::warning)); regenerateButton->setAwesomeIcon(m_isLastMeshGenerationSucceed ? QChar(fa::recycle) : QChar(fa::warning));
}; };
@ -468,6 +470,7 @@ DocumentWindow::DocumentWindow() :
m_openExampleMenu = new QMenu(tr("Open Example")); m_openExampleMenu = new QMenu(tr("Open Example"));
std::vector<QString> exampleModels = { std::vector<QString> exampleModels = {
"Addax", "Addax",
"Backpacker",
"Bicycle", "Bicycle",
"Cat", "Cat",
"Dog", "Dog",
@ -505,6 +508,12 @@ DocumentWindow::DocumentWindow() :
connect(m_showPreferencesAction, &QAction::triggered, this, &DocumentWindow::showPreferences); connect(m_showPreferencesAction, &QAction::triggered, this, &DocumentWindow::showPreferences);
m_fileMenu->addAction(m_showPreferencesAction); m_fileMenu->addAction(m_showPreferencesAction);
m_fileMenu->addSeparator();
m_importAction = new QAction(tr("Import..."), this);
connect(m_importAction, &QAction::triggered, this, &DocumentWindow::import, Qt::QueuedConnection);
m_fileMenu->addAction(m_importAction);
m_fileMenu->addSeparator(); m_fileMenu->addSeparator();
//m_exportMenu = m_fileMenu->addMenu(tr("Export")); //m_exportMenu = m_fileMenu->addMenu(tr("Export"));
@ -568,6 +577,10 @@ DocumentWindow::DocumentWindow() :
connect(m_breakAction, &QAction::triggered, m_graphicsWidget, &SkeletonGraphicsWidget::breakSelected); connect(m_breakAction, &QAction::triggered, m_graphicsWidget, &SkeletonGraphicsWidget::breakSelected);
m_editMenu->addAction(m_breakAction); m_editMenu->addAction(m_breakAction);
m_reverseAction = new QAction(tr("Reverse"), this);
connect(m_reverseAction, &QAction::triggered, m_graphicsWidget, &SkeletonGraphicsWidget::reverseSelectedEdges);
m_editMenu->addAction(m_reverseAction);
m_connectAction = new QAction(tr("Connect"), this); m_connectAction = new QAction(tr("Connect"), this);
connect(m_connectAction, &QAction::triggered, m_graphicsWidget, &SkeletonGraphicsWidget::connectSelected); connect(m_connectAction, &QAction::triggered, m_graphicsWidget, &SkeletonGraphicsWidget::connectSelected);
m_editMenu->addAction(m_connectAction); m_editMenu->addAction(m_connectAction);
@ -710,6 +723,7 @@ DocumentWindow::DocumentWindow() :
m_redoAction->setEnabled(m_document->redoable()); m_redoAction->setEnabled(m_document->redoable());
m_deleteAction->setEnabled(m_graphicsWidget->hasSelection()); m_deleteAction->setEnabled(m_graphicsWidget->hasSelection());
m_breakAction->setEnabled(m_graphicsWidget->hasEdgeSelection()); m_breakAction->setEnabled(m_graphicsWidget->hasEdgeSelection());
m_reverseAction->setEnabled(m_graphicsWidget->hasEdgeSelection());
m_connectAction->setEnabled(m_graphicsWidget->hasTwoDisconnectedNodesSelection()); m_connectAction->setEnabled(m_graphicsWidget->hasTwoDisconnectedNodesSelection());
m_cutAction->setEnabled(m_graphicsWidget->hasSelection()); m_cutAction->setEnabled(m_graphicsWidget->hasSelection());
m_copyAction->setEnabled(m_graphicsWidget->hasSelection()); m_copyAction->setEnabled(m_graphicsWidget->hasSelection());
@ -766,7 +780,7 @@ DocumentWindow::DocumentWindow() :
m_toggleColorAction = new QAction(tr("Toggle Color"), this); m_toggleColorAction = new QAction(tr("Toggle Color"), this);
connect(m_toggleColorAction, &QAction::triggered, [&]() { connect(m_toggleColorAction, &QAction::triggered, [&]() {
m_modelRemoveColor = !m_modelRemoveColor; m_modelRemoveColor = !m_modelRemoveColor;
MeshLoader *mesh = nullptr; Model *mesh = nullptr;
if (m_document->isMeshGenerating() && if (m_document->isMeshGenerating() &&
m_document->isPostProcessing() && m_document->isPostProcessing() &&
m_document->isTextureGenerating()) { m_document->isTextureGenerating()) {
@ -990,6 +1004,7 @@ DocumentWindow::DocumentWindow() :
connect(graphicsWidget, &SkeletonGraphicsWidget::batchChangeBegin, m_document, &Document::batchChangeBegin); connect(graphicsWidget, &SkeletonGraphicsWidget::batchChangeBegin, m_document, &Document::batchChangeBegin);
connect(graphicsWidget, &SkeletonGraphicsWidget::batchChangeEnd, m_document, &Document::batchChangeEnd); connect(graphicsWidget, &SkeletonGraphicsWidget::batchChangeEnd, m_document, &Document::batchChangeEnd);
connect(graphicsWidget, &SkeletonGraphicsWidget::breakEdge, m_document, &Document::breakEdge); connect(graphicsWidget, &SkeletonGraphicsWidget::breakEdge, m_document, &Document::breakEdge);
connect(graphicsWidget, &SkeletonGraphicsWidget::reverseEdge, m_document, &Document::reverseEdge);
connect(graphicsWidget, &SkeletonGraphicsWidget::moveOriginBy, m_document, &Document::moveOriginBy); connect(graphicsWidget, &SkeletonGraphicsWidget::moveOriginBy, m_document, &Document::moveOriginBy);
connect(graphicsWidget, &SkeletonGraphicsWidget::partChecked, m_document, &Document::partChecked); connect(graphicsWidget, &SkeletonGraphicsWidget::partChecked, m_document, &Document::partChecked);
connect(graphicsWidget, &SkeletonGraphicsWidget::partUnchecked, m_document, &Document::partUnchecked); connect(graphicsWidget, &SkeletonGraphicsWidget::partUnchecked, m_document, &Document::partUnchecked);
@ -1026,6 +1041,7 @@ DocumentWindow::DocumentWindow() :
connect(m_document, &Document::nodeRadiusChanged, graphicsWidget, &SkeletonGraphicsWidget::nodeRadiusChanged); connect(m_document, &Document::nodeRadiusChanged, graphicsWidget, &SkeletonGraphicsWidget::nodeRadiusChanged);
connect(m_document, &Document::nodeBoneMarkChanged, graphicsWidget, &SkeletonGraphicsWidget::nodeBoneMarkChanged); connect(m_document, &Document::nodeBoneMarkChanged, graphicsWidget, &SkeletonGraphicsWidget::nodeBoneMarkChanged);
connect(m_document, &Document::nodeOriginChanged, graphicsWidget, &SkeletonGraphicsWidget::nodeOriginChanged); connect(m_document, &Document::nodeOriginChanged, graphicsWidget, &SkeletonGraphicsWidget::nodeOriginChanged);
connect(m_document, &Document::edgeReversed, graphicsWidget, &SkeletonGraphicsWidget::edgeReversed);
connect(m_document, &Document::partVisibleStateChanged, graphicsWidget, &SkeletonGraphicsWidget::partVisibleStateChanged); connect(m_document, &Document::partVisibleStateChanged, graphicsWidget, &SkeletonGraphicsWidget::partVisibleStateChanged);
connect(m_document, &Document::partDisableStateChanged, graphicsWidget, &SkeletonGraphicsWidget::partVisibleStateChanged); connect(m_document, &Document::partDisableStateChanged, graphicsWidget, &SkeletonGraphicsWidget::partVisibleStateChanged);
connect(m_document, &Document::cleanup, graphicsWidget, &SkeletonGraphicsWidget::removeAllContent); connect(m_document, &Document::cleanup, graphicsWidget, &SkeletonGraphicsWidget::removeAllContent);
@ -1470,82 +1486,138 @@ void DocumentWindow::saveTo(const QString &saveAsFilename)
} }
} }
// FIXME: Merge code here and the code in documentsaver.cpp
QApplication::setOverrideCursor(Qt::WaitCursor); QApplication::setOverrideCursor(Qt::WaitCursor);
Ds3FileWriter ds3Writer;
QByteArray modelXml;
QXmlStreamWriter stream(&modelXml);
Snapshot snapshot; Snapshot snapshot;
m_document->toSnapshot(&snapshot); m_document->toSnapshot(&snapshot);
saveSkeletonToXmlStream(&snapshot, &stream); if (DocumentSaver::save(&filename,
if (modelXml.size() > 0) &snapshot,
ds3Writer.add("model.xml", "model", &modelXml); (!m_document->turnaround.isNull() && m_document->turnaroundPngByteArray.size() > 0) ?
&m_document->turnaroundPngByteArray : nullptr,
(!m_document->script().isEmpty()) ? &m_document->script() : nullptr,
(!m_document->variables().empty()) ? &m_document->variables() : nullptr)) {
setCurrentFilename(filename);
}
QApplication::restoreOverrideCursor();
}
if (!m_document->turnaround.isNull() && m_document->turnaroundPngByteArray.size() > 0) { void DocumentWindow::importPath(const QString &path)
ds3Writer.add("canvas.png", "asset", &m_document->turnaroundPngByteArray); {
} QApplication::setOverrideCursor(Qt::WaitCursor);
Ds3FileReader ds3Reader(path);
bool documentChanged = false;
if (!m_document->script().isEmpty()) { for (int i = 0; i < ds3Reader.items().size(); ++i) {
auto script = m_document->script().toUtf8(); Ds3ReaderItem item = ds3Reader.items().at(i);
ds3Writer.add("model.js", "script", &script); if (item.type == "asset") {
} if (item.name.startsWith("images/")) {
QString filename = item.name.split("/")[1];
const auto &variables = m_document->variables(); QString imageIdString = filename.split(".")[0];
if (!variables.empty()) { QUuid imageId = QUuid(imageIdString);
QByteArray variablesXml; if (!imageId.isNull()) {
QXmlStreamWriter variablesXmlStream(&variablesXml); QByteArray data;
saveVariablesToXmlStream(variables, &variablesXmlStream); ds3Reader.loadItem(item.name, &data);
if (variablesXml.size() > 0) QImage image = QImage::fromData(data, "PNG");
ds3Writer.add("variables.xml", "variable", &variablesXml); (void)ImageForever::add(&image, imageId);
} }
std::set<QUuid> imageIds;
for (const auto &material: snapshot.materials) {
for (auto &layer: material.second) {
for (auto &mapItem: layer.second) {
auto findImageIdString = mapItem.find("linkData");
if (findImageIdString == mapItem.end())
continue;
QUuid imageId = QUuid(findImageIdString->second);
imageIds.insert(imageId);
} }
} }
} }
for (const auto &part: snapshot.parts) {
auto findImageIdString = part.second.find("deformMapImageId");
if (findImageIdString == part.second.end())
continue;
QUuid imageId = QUuid(findImageIdString->second);
imageIds.insert(imageId);
}
for (auto &pose: snapshot.poses) { for (int i = 0; i < ds3Reader.items().size(); ++i) {
auto findCanvasImageId = pose.first.find("canvasImageId"); Ds3ReaderItem item = ds3Reader.items().at(i);
if (findCanvasImageId != pose.first.end()) { if (item.type == "model") {
QUuid imageId = QUuid(findCanvasImageId->second); {
imageIds.insert(imageId); QByteArray data;
ds3Reader.loadItem(item.name, &data);
QXmlStreamReader stream(data);
Snapshot snapshot;
loadSkeletonFromXmlStream(&snapshot, stream, SNAPSHOT_ITEM_MATERIAL);
m_document->addFromSnapshot(snapshot, Document::SnapshotSource::Import);
documentChanged = true;
}
{
QByteArray data;
ds3Reader.loadItem(item.name, &data);
QXmlStreamReader stream(data);
Snapshot snapshot;
loadSkeletonFromXmlStream(&snapshot, stream, SNAPSHOT_ITEM_CANVAS | SNAPSHOT_ITEM_COMPONENT);
QByteArray modelXml;
QXmlStreamWriter modelStream(&modelXml);
saveSkeletonToXmlStream(&snapshot, &modelStream);
if (modelXml.size() > 0) {
QUuid fillMeshFileId = FileForever::add(item.name, modelXml);
if (!fillMeshFileId.isNull()) {
Snapshot partSnapshot;
createPartSnapshotForFillMesh(fillMeshFileId, &partSnapshot);
m_document->addFromSnapshot(partSnapshot, Document::SnapshotSource::Paste);
documentChanged = true;
}
}
}
} }
} }
for (const auto &imageId: imageIds) { if (documentChanged)
const QByteArray *pngByteArray = ImageForever::getPngByteArray(imageId); m_document->saveSnapshot();
if (nullptr == pngByteArray)
continue;
if (pngByteArray->size() > 0)
ds3Writer.add("images/" + imageId.toString() + ".png", "asset", pngByteArray);
}
if (ds3Writer.save(filename)) {
setCurrentFilename(filename);
}
QApplication::restoreOverrideCursor(); QApplication::restoreOverrideCursor();
} }
void DocumentWindow::createPartSnapshotForFillMesh(const QUuid &fillMeshFileId, Snapshot *snapshot)
{
if (fillMeshFileId.isNull())
return;
auto partId = QUuid::createUuid();
auto partIdString = partId.toString();
std::map<QString, QString> snapshotPart;
snapshotPart["id"] = partIdString;
snapshotPart["fillMesh"] = fillMeshFileId.toString();
snapshot->parts[partIdString] = snapshotPart;
auto componentId = QUuid::createUuid();
auto componentIdString = componentId.toString();
std::map<QString, QString> snapshotComponent;
snapshotComponent["id"] = componentIdString;
snapshotComponent["combineMode"] = "Uncombined";
snapshotComponent["linkDataType"] = "partId";
snapshotComponent["linkData"] = partIdString;
snapshot->components[componentIdString] = snapshotComponent;
snapshot->rootComponent["children"] = componentIdString;
auto createNode = [&](const QVector3D &position, float radius) {
auto nodeId = QUuid::createUuid();
auto nodeIdString = nodeId.toString();
std::map<QString, QString> snapshotNode;
snapshotNode["id"] = nodeIdString;
snapshotNode["x"] = QString::number(position.x());
snapshotNode["y"] = QString::number(position.y());
snapshotNode["z"] = QString::number(position.z());
snapshotNode["radius"] = QString::number(radius);
snapshotNode["partId"] = partIdString;
snapshot->nodes[nodeIdString] = snapshotNode;
return nodeIdString;
};
auto createEdge = [&](const QString &fromNode, const QString &toNode) {
auto edgeId = QUuid::createUuid();
auto edgeIdString = edgeId.toString();
std::map<QString, QString> snapshotEdge;
snapshotEdge["id"] = edgeIdString;
snapshotEdge["from"] = fromNode;
snapshotEdge["to"] = toNode;
snapshotEdge["partId"] = partIdString;
snapshot->edges[edgeIdString] = snapshotEdge;
};
createEdge(createNode(QVector3D(0.5, 0.5, 1.0), 0.1),
createNode(QVector3D(0.5, 0.3, 1.0), 0.1));
}
void DocumentWindow::openPathAs(const QString &path, const QString &asName) void DocumentWindow::openPathAs(const QString &path, const QString &asName)
{ {
QApplication::setOverrideCursor(Qt::WaitCursor); QApplication::setOverrideCursor(Qt::WaitCursor);
@ -1569,6 +1641,15 @@ void DocumentWindow::openPathAs(const QString &path, const QString &asName)
QImage image = QImage::fromData(data, "PNG"); QImage image = QImage::fromData(data, "PNG");
(void)ImageForever::add(&image, imageId); (void)ImageForever::add(&image, imageId);
} }
} else if (item.name.startsWith("files/")) {
QString filename = item.name.split("/")[1];
QString fileIdString = filename.split(".")[0];
QUuid fileId = QUuid(fileIdString);
if (!fileId.isNull()) {
QByteArray data;
ds3Reader.loadItem(item.name, &data);
(void)FileForever::add(item.name, data, fileId);
}
} }
} }
} }
@ -1682,7 +1763,7 @@ void DocumentWindow::exportObjResult()
void DocumentWindow::exportObjToFilename(const QString &filename) void DocumentWindow::exportObjToFilename(const QString &filename)
{ {
QApplication::setOverrideCursor(Qt::WaitCursor); QApplication::setOverrideCursor(Qt::WaitCursor);
MeshLoader *resultMesh = m_document->takeResultMesh(); Model *resultMesh = m_document->takeResultMesh();
if (nullptr != resultMesh) { if (nullptr != resultMesh) {
resultMesh->exportAsObj(filename); resultMesh->exportAsObj(filename);
delete resultMesh; delete resultMesh;
@ -1822,7 +1903,7 @@ void DocumentWindow::updateRadiusLockButtonState()
void DocumentWindow::updateRigWeightRenderWidget() void DocumentWindow::updateRigWeightRenderWidget()
{ {
MeshLoader *resultRigWeightMesh = m_document->takeResultRigWeightMesh(); Model *resultRigWeightMesh = m_document->takeResultRigWeightMesh();
if (nullptr == resultRigWeightMesh) { if (nullptr == resultRigWeightMesh) {
m_rigWidget->rigWeightRenderWidget()->hide(); m_rigWidget->rigWeightRenderWidget()->hide();
} else { } else {
@ -1984,17 +2065,17 @@ void DocumentWindow::checkExportWaitingList()
auto list = m_waitingForExportToFilenames; auto list = m_waitingForExportToFilenames;
m_waitingForExportToFilenames.clear(); m_waitingForExportToFilenames.clear();
bool isSucceed = m_document->isMeshGenerationSucceed(); bool isSuccessful = m_document->isMeshGenerationSucceed();
for (const auto &filename: list) { for (const auto &filename: list) {
if (filename.endsWith(".obj")) { if (filename.endsWith(".obj")) {
exportObjToFilename(filename); exportObjToFilename(filename);
emit waitingExportFinished(filename, isSucceed); emit waitingExportFinished(filename, isSuccessful);
} else if (filename.endsWith(".fbx")) { } else if (filename.endsWith(".fbx")) {
exportFbxToFilename(filename); exportFbxToFilename(filename);
emit waitingExportFinished(filename, isSucceed); emit waitingExportFinished(filename, isSuccessful);
} else if (filename.endsWith(".glb")) { } else if (filename.endsWith(".glb")) {
exportGlbToFilename(filename); exportGlbToFilename(filename);
emit waitingExportFinished(filename, isSucceed); emit waitingExportFinished(filename, isSuccessful);
} else { } else {
emit waitingExportFinished(filename, false); emit waitingExportFinished(filename, false);
} }
@ -2010,14 +2091,14 @@ void DocumentWindow::normalAndDepthMapsReady()
{ {
QImage *normalMap = m_normalAndDepthMapsGenerator->takeNormalMap(); QImage *normalMap = m_normalAndDepthMapsGenerator->takeNormalMap();
QImage *depthMap = m_normalAndDepthMapsGenerator->takeDepthMap(); QImage *depthMap = m_normalAndDepthMapsGenerator->takeDepthMap();
m_modelRenderWidget->updateToonNormalAndDepthMaps(normalMap, depthMap); m_modelRenderWidget->updateToonNormalAndDepthMaps(normalMap, depthMap);
//m_normalAndDepthMapsGenerator->setRenderThread(QGuiApplication::instance()->thread());
delete m_normalAndDepthMapsGenerator; delete m_normalAndDepthMapsGenerator;
m_normalAndDepthMapsGenerator = nullptr; m_normalAndDepthMapsGenerator = nullptr;
qDebug() << "Normal and depth maps generation done";
if (m_isNormalAndDepthMapsObsolete) { if (m_isNormalAndDepthMapsObsolete) {
generateNormalAndDepthMaps(); generateNormalAndDepthMaps();
} }
@ -2036,21 +2117,18 @@ void DocumentWindow::generateNormalAndDepthMaps()
if (nullptr == resultMesh) if (nullptr == resultMesh)
return; return;
qDebug() << "Normal and depth maps generating...";
QThread *thread = new QThread; QThread *thread = new QThread;
m_normalAndDepthMapsGenerator = new NormalAndDepthMapsGenerator(m_modelRenderWidget); m_normalAndDepthMapsGenerator = new NormalAndDepthMapsGenerator(m_modelRenderWidget);
m_normalAndDepthMapsGenerator->updateMesh(resultMesh); m_normalAndDepthMapsGenerator->updateMesh(resultMesh);
m_normalAndDepthMapsGenerator->moveToThread(thread); m_normalAndDepthMapsGenerator->moveToThread(thread);
//m_normalAndDepthMapsGenerator->setRenderThread(thread); m_normalAndDepthMapsGenerator->setRenderThread(thread);
connect(thread, &QThread::started, m_normalAndDepthMapsGenerator, &NormalAndDepthMapsGenerator::process); connect(thread, &QThread::started, m_normalAndDepthMapsGenerator, &NormalAndDepthMapsGenerator::process);
connect(m_normalAndDepthMapsGenerator, &NormalAndDepthMapsGenerator::finished, this, &DocumentWindow::normalAndDepthMapsReady); connect(m_normalAndDepthMapsGenerator, &NormalAndDepthMapsGenerator::finished, this, &DocumentWindow::normalAndDepthMapsReady);
connect(m_normalAndDepthMapsGenerator, &NormalAndDepthMapsGenerator::finished, thread, &QThread::quit); connect(m_normalAndDepthMapsGenerator, &NormalAndDepthMapsGenerator::finished, thread, &QThread::quit);
connect(thread, &QThread::finished, thread, &QThread::deleteLater); connect(thread, &QThread::finished, thread, &QThread::deleteLater);
thread->start(); thread->start();
//m_normalAndDepthMapsGenerator = new NormalAndDepthMapsGenerator(m_modelRenderWidget);
//m_normalAndDepthMapsGenerator->updateMesh(resultMesh);
//connect(m_normalAndDepthMapsGenerator, &NormalAndDepthMapsGenerator::finished, this, &DocumentWindow::normalAndDepthMapsReady);
//m_normalAndDepthMapsGenerator->process();
} }
void DocumentWindow::delayedGenerateNormalAndDepthMaps() void DocumentWindow::delayedGenerateNormalAndDepthMaps()
@ -2058,22 +2136,15 @@ void DocumentWindow::delayedGenerateNormalAndDepthMaps()
if (!Preferences::instance().toonShading()) if (!Preferences::instance().toonShading())
return; return;
//delete m_normalAndDepthMapsDelayTimer; generateNormalAndDepthMaps();
//m_normalAndDepthMapsDelayTimer = new QTimer(this);
//m_normalAndDepthMapsDelayTimer->setSingleShot(true);
//m_normalAndDepthMapsDelayTimer->setInterval(250);
//connect(m_normalAndDepthMapsDelayTimer, &QTimer::timeout, [=] {
generateNormalAndDepthMaps();
//});
//m_normalAndDepthMapsDelayTimer->start();
} }
void DocumentWindow::exportImageToFilename(const QString &filename) void DocumentWindow::exportImageToFilename(const QString &filename)
{ {
QApplication::setOverrideCursor(Qt::WaitCursor); QApplication::setOverrideCursor(Qt::WaitCursor);
MeshLoader *resultMesh = m_modelRenderWidget->fetchCurrentMesh(); Model *resultMesh = m_modelRenderWidget->fetchCurrentMesh();
if (nullptr != resultMesh) { if (nullptr != resultMesh) {
ModelOfflineRender *offlineRender = new ModelOfflineRender(m_modelRenderWidget->format()); ModelOffscreenRender *offlineRender = new ModelOffscreenRender(m_modelRenderWidget->format());
offlineRender->setXRotation(m_modelRenderWidget->xRot()); offlineRender->setXRotation(m_modelRenderWidget->xRot());
offlineRender->setYRotation(m_modelRenderWidget->yRot()); offlineRender->setYRotation(m_modelRenderWidget->yRot());
offlineRender->setZRotation(m_modelRenderWidget->zRot()); offlineRender->setZRotation(m_modelRenderWidget->zRot());
@ -2095,3 +2166,12 @@ void DocumentWindow::exportImageToFilename(const QString &filename)
} }
QApplication::restoreOverrideCursor(); QApplication::restoreOverrideCursor();
} }
void DocumentWindow::import()
{
QString fileName = QFileDialog::getOpenFileName(this, QString(), QString(),
tr("Dust3D Document (*.ds3)")).trimmed();
if (fileName.isEmpty())
return;
importPath(fileName);
}

View File

@ -10,7 +10,6 @@
#include <map> #include <map>
#include <QStringList> #include <QStringList>
#include <QLabel> #include <QLabel>
#include <QTimer>
#include "document.h" #include "document.h"
#include "modelwidget.h" #include "modelwidget.h"
#include "exportpreviewwidget.h" #include "exportpreviewwidget.h"
@ -30,7 +29,7 @@ class DocumentWindow : public QMainWindow
signals: signals:
void initialized(); void initialized();
void uninialized(); void uninialized();
void waitingExportFinished(const QString &filename, bool succeed); void waitingExportFinished(const QString &filename, bool isSuccessful);
void mouseTargetVertexPositionChanged(const QVector3D &position); void mouseTargetVertexPositionChanged(const QVector3D &position);
public: public:
DocumentWindow(); DocumentWindow();
@ -93,10 +92,13 @@ public slots:
void delayedGenerateNormalAndDepthMaps(); void delayedGenerateNormalAndDepthMaps();
void normalAndDepthMapsReady(); void normalAndDepthMapsReady();
void autoRecover(); void autoRecover();
void import();
void importPath(const QString &filename);
private: private:
void initLockButton(QPushButton *button); void initLockButton(QPushButton *button);
void setCurrentFilename(const QString &filename); void setCurrentFilename(const QString &filename);
void updateTitle(); void updateTitle();
void createPartSnapshotForFillMesh(const QUuid &fillMeshFileId, Snapshot *snapshot);
private: private:
Document *m_document; Document *m_document;
bool m_firstShow; bool m_firstShow;
@ -130,6 +132,8 @@ private:
QAction *m_changeTurnaroundAction; QAction *m_changeTurnaroundAction;
QAction *m_quitAction; QAction *m_quitAction;
QAction *m_importAction;
QAction *m_exportAsObjAction; QAction *m_exportAsObjAction;
QAction *m_exportAsObjPlusMaterialsAction; QAction *m_exportAsObjPlusMaterialsAction;
QAction *m_exportAction; QAction *m_exportAction;
@ -141,6 +145,7 @@ private:
QAction *m_redoAction; QAction *m_redoAction;
QAction *m_deleteAction; QAction *m_deleteAction;
QAction *m_breakAction; QAction *m_breakAction;
QAction *m_reverseAction;
QAction *m_connectAction; QAction *m_connectAction;
QAction *m_cutAction; QAction *m_cutAction;
QAction *m_copyAction; QAction *m_copyAction;
@ -211,7 +216,6 @@ private:
QMetaObject::Connection m_partListDockerVisibleSwitchConnection; QMetaObject::Connection m_partListDockerVisibleSwitchConnection;
NormalAndDepthMapsGenerator *m_normalAndDepthMapsGenerator = nullptr; NormalAndDepthMapsGenerator *m_normalAndDepthMapsGenerator = nullptr;
QTimer *m_normalAndDepthMapsDelayTimer = nullptr;
bool m_isNormalAndDepthMapsObsolete = false; bool m_isNormalAndDepthMapsObsolete = false;
AutoSaver *m_autoSaver = nullptr; AutoSaver *m_autoSaver = nullptr;

View File

@ -9,7 +9,7 @@
#include "version.h" #include "version.h"
#include "util.h" #include "util.h"
#include "jointnodetree.h" #include "jointnodetree.h"
#include "meshloader.h" #include "model.h"
// Play with glTF online: // Play with glTF online:
// https://gltf-viewer.donmccurdy.com/ // https://gltf-viewer.donmccurdy.com/
@ -168,8 +168,8 @@ GlbFileWriter::GlbFileWriter(Outcome &outcome,
} }
int textureIndex = 0; int textureIndex = 0;
m_json["materials"][primitiveIndex]["pbrMetallicRoughness"]["baseColorTexture"]["index"] = textureIndex++; m_json["materials"][primitiveIndex]["pbrMetallicRoughness"]["baseColorTexture"]["index"] = textureIndex++;
m_json["materials"][primitiveIndex]["pbrMetallicRoughness"]["metallicFactor"] = MeshLoader::m_defaultMetalness; m_json["materials"][primitiveIndex]["pbrMetallicRoughness"]["metallicFactor"] = Model::m_defaultMetalness;
m_json["materials"][primitiveIndex]["pbrMetallicRoughness"]["roughnessFactor"] = MeshLoader::m_defaultRoughness; m_json["materials"][primitiveIndex]["pbrMetallicRoughness"]["roughnessFactor"] = Model::m_defaultRoughness;
if (textureHasTransparencySettings) if (textureHasTransparencySettings)
m_json["materials"][primitiveIndex]["alphaMode"] = "BLEND"; m_json["materials"][primitiveIndex]["alphaMode"] = "BLEND";
if (normalImage) { if (normalImage) {

View File

@ -1,20 +1,29 @@
#include "logbrowser.h"
// Modified from https://wiki.qt.io/Browser_for_QDebug_output // Modified from https://wiki.qt.io/Browser_for_QDebug_output
#include <QMetaType> #include <QMetaType>
#include <stdio.h> #include <QDir>
#include "logbrowser.h"
#include "logbrowserdialog.h" #include "logbrowserdialog.h"
bool LogBrowser::m_enableOutputToFile = false;
LogBrowser::LogBrowser(QObject *parent) : LogBrowser::LogBrowser(QObject *parent) :
QObject(parent) QObject(parent)
{ {
qRegisterMetaType<QtMsgType>("QtMsgType"); qRegisterMetaType<QtMsgType>("QtMsgType");
m_browserDialog = new LogBrowserDialog; m_browserDialog = new LogBrowserDialog;
connect(this, &LogBrowser::sendMessage, m_browserDialog, &LogBrowserDialog::outputMessage, Qt::QueuedConnection); connect(this, &LogBrowser::sendMessage, m_browserDialog, &LogBrowserDialog::outputMessage, Qt::QueuedConnection);
if (m_enableOutputToFile) {
QString filePath = "dust3d.log";
m_outputTo = fopen(filePath.toUtf8().constData(), "w");
}
} }
LogBrowser::~LogBrowser() LogBrowser::~LogBrowser()
{ {
delete m_browserDialog; delete m_browserDialog;
if (m_outputTo)
fclose(m_outputTo);
} }
void LogBrowser::showDialog() void LogBrowser::showDialog()
@ -36,5 +45,9 @@ bool LogBrowser::isDialogVisible()
void LogBrowser::outputMessage(QtMsgType type, const QString &msg, const QString &source, int line) void LogBrowser::outputMessage(QtMsgType type, const QString &msg, const QString &source, int line)
{ {
if (m_outputTo) {
fprintf(m_outputTo, "[%s:%d]: %s\n", source.toUtf8().constData(), line, msg.toUtf8().constData());
fflush(m_outputTo);
}
emit sendMessage(type, msg, source, line); emit sendMessage(type, msg, source, line);
} }

View File

@ -2,6 +2,7 @@
#define DUST3D_LOG_BROWSER_H #define DUST3D_LOG_BROWSER_H
// Modified from https://wiki.qt.io/Browser_for_QDebug_output // Modified from https://wiki.qt.io/Browser_for_QDebug_output
#include <QObject> #include <QObject>
#include <cstdio>
class LogBrowserDialog; class LogBrowserDialog;
@ -9,7 +10,7 @@ class LogBrowser : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit LogBrowser(QObject *parent = 0); explicit LogBrowser(QObject *parent=0);
~LogBrowser(); ~LogBrowser();
public slots: public slots:
@ -22,7 +23,9 @@ signals:
void sendMessage(QtMsgType type, const QString &msg, const QString &source, int line); void sendMessage(QtMsgType type, const QString &msg, const QString &source, int line);
private: private:
LogBrowserDialog *m_browserDialog; LogBrowserDialog *m_browserDialog = nullptr;
FILE *m_outputTo = nullptr;
static bool m_enableOutputToFile;
}; };
#endif // LOGBROWSER_H #endif // LOGBROWSER_H

View File

@ -97,10 +97,10 @@ int main(int argc, char ** argv)
openFileList.size() == 1) { openFileList.size() == 1) {
totalExportFileNum = openFileList.size() * waitingExportList.size(); totalExportFileNum = openFileList.size() * waitingExportList.size();
for (int i = 0; i < openFileList.size(); ++i) { for (int i = 0; i < openFileList.size(); ++i) {
QObject::connect(windowList[i], &DocumentWindow::waitingExportFinished, &app, [&](const QString &filename, bool succeed) { QObject::connect(windowList[i], &DocumentWindow::waitingExportFinished, &app, [&](const QString &filename, bool isSuccessful) {
qDebug() << "Export to" << filename << (succeed ? "succeed" : "failed"); qDebug() << "Export to" << filename << (isSuccessful ? "isSuccessful" : "failed");
++finishedExportFileNum; ++finishedExportFileNum;
if (succeed) if (isSuccessful)
++succeedExportNum; ++succeedExportNum;
if (finishedExportFileNum == totalExportFileNum) { if (finishedExportFileNum == totalExportFileNum) {
if (succeedExportNum == totalExportFileNum) { if (succeedExportNum == totalExportFileNum) {

View File

@ -29,9 +29,9 @@ const std::set<QUuid> &MaterialPreviewsGenerator::generatedPreviewMaterialIds()
return m_generatedMaterialIds; return m_generatedMaterialIds;
} }
MeshLoader *MaterialPreviewsGenerator::takePreview(QUuid materialId) Model *MaterialPreviewsGenerator::takePreview(QUuid materialId)
{ {
MeshLoader *resultMesh = m_previews[materialId]; Model *resultMesh = m_previews[materialId];
m_previews[materialId] = nullptr; m_previews[materialId] = nullptr;
return resultMesh; return resultMesh;
} }
@ -96,9 +96,9 @@ void MaterialPreviewsGenerator::generate()
} }
} }
textureGenerator->generate(); textureGenerator->generate();
MeshLoader *texturedResultMesh = textureGenerator->takeResultMesh(); Model *texturedResultMesh = textureGenerator->takeResultMesh();
if (nullptr != texturedResultMesh) { if (nullptr != texturedResultMesh) {
m_previews[material.first] = new MeshLoader(*texturedResultMesh); m_previews[material.first] = new Model(*texturedResultMesh);
m_generatedMaterialIds.insert(material.first); m_generatedMaterialIds.insert(material.first);
delete texturedResultMesh; delete texturedResultMesh;
} }

View File

@ -4,7 +4,7 @@
#include <map> #include <map>
#include <QUuid> #include <QUuid>
#include <vector> #include <vector>
#include "meshloader.h" #include "model.h"
#include "document.h" #include "document.h"
class MaterialPreviewsGenerator : public QObject class MaterialPreviewsGenerator : public QObject
@ -15,7 +15,7 @@ public:
~MaterialPreviewsGenerator(); ~MaterialPreviewsGenerator();
void addMaterial(QUuid materialId, const std::vector<MaterialLayer> &layers); void addMaterial(QUuid materialId, const std::vector<MaterialLayer> &layers);
const std::set<QUuid> &generatedPreviewMaterialIds(); const std::set<QUuid> &generatedPreviewMaterialIds();
MeshLoader *takePreview(QUuid materialId); Model *takePreview(QUuid materialId);
void generate(); void generate();
signals: signals:
void finished(); void finished();
@ -23,7 +23,7 @@ public slots:
void process(); void process();
private: private:
std::vector<std::pair<QUuid, std::vector<MaterialLayer>>> m_materials; std::vector<std::pair<QUuid, std::vector<MaterialLayer>>> m_materials;
std::map<QUuid, MeshLoader *> m_previews; std::map<QUuid, Model *> m_previews;
std::set<QUuid> m_generatedMaterialIds; std::set<QUuid> m_generatedMaterialIds;
}; };

View File

@ -77,7 +77,7 @@ void MaterialWidget::updatePreview(QUuid materialId)
qDebug() << "Material not found:" << m_materialId; qDebug() << "Material not found:" << m_materialId;
return; return;
} }
MeshLoader *previewMesh = material->takePreviewMesh(); Model *previewMesh = material->takePreviewMesh();
m_previewWidget->updateMesh(previewMesh); m_previewWidget->updateMesh(previewMesh);
} }

View File

@ -22,6 +22,9 @@
#include "projectfacestonodes.h" #include "projectfacestonodes.h"
#include "document.h" #include "document.h"
#include "simulateclothmeshes.h" #include "simulateclothmeshes.h"
#include "meshstroketifier.h"
#include "fileforever.h"
#include "snapshotxml.h"
MeshGenerator::MeshGenerator(Snapshot *snapshot) : MeshGenerator::MeshGenerator(Snapshot *snapshot) :
m_snapshot(snapshot) m_snapshot(snapshot)
@ -49,21 +52,21 @@ quint64 MeshGenerator::id()
return m_id; return m_id;
} }
bool MeshGenerator::isSucceed() bool MeshGenerator::isSuccessful()
{ {
return m_isSucceed; return m_isSuccessful;
} }
MeshLoader *MeshGenerator::takeResultMesh() Model *MeshGenerator::takeResultMesh()
{ {
MeshLoader *resultMesh = m_resultMesh; Model *resultMesh = m_resultMesh;
m_resultMesh = nullptr; m_resultMesh = nullptr;
return resultMesh; return resultMesh;
} }
MeshLoader *MeshGenerator::takePartPreviewMesh(const QUuid &partId) Model *MeshGenerator::takePartPreviewMesh(const QUuid &partId)
{ {
MeshLoader *resultMesh = m_partPreviewMeshes[partId]; Model *resultMesh = m_partPreviewMeshes[partId];
m_partPreviewMeshes[partId] = nullptr; m_partPreviewMeshes[partId] = nullptr;
return resultMesh; return resultMesh;
} }
@ -325,7 +328,7 @@ void MeshGenerator::cutFaceStringToCutTemplate(const QString &cutFaceString, std
} }
} }
MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString, bool *hasError, bool addIntermediateNodes) MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString, bool *hasError, bool *retryable, bool addIntermediateNodes)
{ {
auto findPart = m_snapshot->parts.find(partIdString); auto findPart = m_snapshot->parts.find(partIdString);
if (findPart == m_snapshot->parts.end()) { if (findPart == m_snapshot->parts.end()) {
@ -335,6 +338,9 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString,
QUuid partId = QUuid(partIdString); QUuid partId = QUuid(partIdString);
auto &part = findPart->second; auto &part = findPart->second;
*retryable = true;
bool isDisabled = isTrueValueString(valueOfKeyInMapOrEmpty(part, "disabled")); bool isDisabled = isTrueValueString(valueOfKeyInMapOrEmpty(part, "disabled"));
bool xMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(part, "xMirrored")); bool xMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(part, "xMirrored"));
bool subdived = isTrueValueString(valueOfKeyInMapOrEmpty(part, "subdived")); bool subdived = isTrueValueString(valueOfKeyInMapOrEmpty(part, "subdived"));
@ -349,7 +355,6 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString,
float hollowThickness = 0.0; float hollowThickness = 0.0;
auto target = PartTargetFromString(valueOfKeyInMapOrEmpty(part, "target").toUtf8().constData()); auto target = PartTargetFromString(valueOfKeyInMapOrEmpty(part, "target").toUtf8().constData());
auto base = PartBaseFromString(valueOfKeyInMapOrEmpty(part, "base").toUtf8().constData()); auto base = PartBaseFromString(valueOfKeyInMapOrEmpty(part, "base").toUtf8().constData());
//bool gridded = isTrueValueString(valueOfKeyInMapOrEmpty(part, "gridded"));
QString cutFaceString = valueOfKeyInMapOrEmpty(part, "cutFace"); QString cutFaceString = valueOfKeyInMapOrEmpty(part, "cutFace");
std::vector<QVector2D> cutTemplate; std::vector<QVector2D> cutTemplate;
@ -404,6 +409,16 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString,
if (!colorSolubilityString.isEmpty()) if (!colorSolubilityString.isEmpty())
colorSolubility = colorSolubilityString.toFloat(); colorSolubility = colorSolubilityString.toFloat();
QUuid fillMeshFileId;
QString fillMeshString = valueOfKeyInMapOrEmpty(part, "fillMesh");
if (!fillMeshString.isEmpty()) {
fillMeshFileId = QUuid(fillMeshString);
if (!fillMeshFileId.isNull()) {
*retryable = false;
xMirrored = false;
}
}
auto &partCache = m_cacheContext->parts[partIdString]; auto &partCache = m_cacheContext->parts[partIdString];
partCache.outcomeNodes.clear(); partCache.outcomeNodes.clear();
partCache.outcomeEdges.clear(); partCache.outcomeEdges.clear();
@ -414,7 +429,7 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString,
partCache.faces.clear(); partCache.faces.clear();
partCache.previewTriangles.clear(); partCache.previewTriangles.clear();
partCache.previewVertices.clear(); partCache.previewVertices.clear();
partCache.isSucceed = false; partCache.isSuccessful = false;
partCache.joined = (target == PartTarget::Model && !isDisabled); partCache.joined = (target == PartTarget::Model && !isDisabled);
delete partCache.mesh; delete partCache.mesh;
partCache.mesh = nullptr; partCache.mesh = nullptr;
@ -497,8 +512,7 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString,
bool buildSucceed = false; bool buildSucceed = false;
std::map<QString, int> nodeIdStringToIndexMap; std::map<QString, int> nodeIdStringToIndexMap;
std::map<int, QString> nodeIndexToIdStringMap; std::map<int, QString> nodeIndexToIdStringMap;
StrokeModifier *nodeMeshModifier = nullptr; StrokeModifier *strokeModifier = nullptr;
StrokeMeshBuilder *nodeMeshBuilder = nullptr;
QString mirroredPartIdString; QString mirroredPartIdString;
QUuid mirroredPartId; QUuid mirroredPartId;
@ -508,7 +522,7 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString,
m_cacheContext->partMirrorIdMap[mirroredPartIdString] = partIdString; m_cacheContext->partMirrorIdMap[mirroredPartIdString] = partIdString;
} }
auto addNode = [&](const QString &nodeIdString, const NodeInfo &nodeInfo) { auto addNodeToPartCache = [&](const QString &nodeIdString, const NodeInfo &nodeInfo) {
OutcomeNode outcomeNode; OutcomeNode outcomeNode;
outcomeNode.partId = QUuid(partIdString); outcomeNode.partId = QUuid(partIdString);
outcomeNode.nodeId = QUuid(nodeIdString); outcomeNode.nodeId = QUuid(nodeIdString);
@ -519,7 +533,7 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString,
outcomeNode.countershaded = countershaded; outcomeNode.countershaded = countershaded;
outcomeNode.colorSolubility = colorSolubility; outcomeNode.colorSolubility = colorSolubility;
outcomeNode.boneMark = nodeInfo.boneMark; outcomeNode.boneMark = nodeInfo.boneMark;
outcomeNode.mirroredByPartId = mirroredPartIdString; outcomeNode.mirroredByPartId = mirroredPartId;
outcomeNode.joined = partCache.joined; outcomeNode.joined = partCache.joined;
partCache.outcomeNodes.push_back(outcomeNode); partCache.outcomeNodes.push_back(outcomeNode);
if (xMirrored) { if (xMirrored) {
@ -530,7 +544,7 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString,
partCache.outcomeNodes.push_back(outcomeNode); partCache.outcomeNodes.push_back(outcomeNode);
} }
}; };
auto addEdge = [&](const QString &firstNodeIdString, const QString &secondNodeIdString) { auto addEdgeToPartCache = [&](const QString &firstNodeIdString, const QString &secondNodeIdString) {
partCache.outcomeEdges.push_back({ partCache.outcomeEdges.push_back({
{QUuid(partIdString), QUuid(firstNodeIdString)}, {QUuid(partIdString), QUuid(firstNodeIdString)},
{QUuid(partIdString), QUuid(secondNodeIdString)} {QUuid(partIdString), QUuid(secondNodeIdString)}
@ -543,10 +557,10 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString,
} }
}; };
nodeMeshModifier = new StrokeModifier; strokeModifier = new StrokeModifier;
if (addIntermediateNodes) if (addIntermediateNodes)
nodeMeshModifier->enableIntermediateAddition(); strokeModifier->enableIntermediateAddition();
for (const auto &nodeIt: nodeInfos) { for (const auto &nodeIt: nodeInfos) {
const auto &nodeIdString = nodeIt.first; const auto &nodeIdString = nodeIt.first;
@ -557,14 +571,12 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString,
cutFaceStringToCutTemplate(nodeInfo.cutFace, nodeCutTemplate); cutFaceStringToCutTemplate(nodeInfo.cutFace, nodeCutTemplate);
if (chamfered) if (chamfered)
chamferFace2D(&nodeCutTemplate); chamferFace2D(&nodeCutTemplate);
nodeIndex = nodeMeshModifier->addNode(nodeInfo.position, nodeInfo.radius, nodeCutTemplate, nodeInfo.cutRotation); nodeIndex = strokeModifier->addNode(nodeInfo.position, nodeInfo.radius, nodeCutTemplate, nodeInfo.cutRotation);
} else { } else {
nodeIndex = nodeMeshModifier->addNode(nodeInfo.position, nodeInfo.radius, cutTemplate, cutRotation); nodeIndex = strokeModifier->addNode(nodeInfo.position, nodeInfo.radius, cutTemplate, cutRotation);
} }
nodeIdStringToIndexMap[nodeIdString] = nodeIndex; nodeIdStringToIndexMap[nodeIdString] = nodeIndex;
nodeIndexToIdStringMap[nodeIndex] = nodeIdString; nodeIndexToIdStringMap[nodeIndex] = nodeIdString;
addNode(nodeIdString, nodeInfo);
} }
for (const auto &edgeIt: edges) { for (const auto &edgeIt: edges) {
@ -583,76 +595,100 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString,
continue; continue;
} }
nodeMeshModifier->addEdge(findFromNodeIndex->second, findToNodeIndex->second); strokeModifier->addEdge(findFromNodeIndex->second, findToNodeIndex->second);
addEdge(fromNodeIdString, toNodeIdString);
} }
if (subdived) if (subdived)
nodeMeshModifier->subdivide(); strokeModifier->subdivide();
if (rounded) if (rounded)
nodeMeshModifier->roundEnd(); strokeModifier->roundEnd();
nodeMeshModifier->finalize(); strokeModifier->finalize();
nodeMeshBuilder = new StrokeMeshBuilder; std::vector<size_t> sourceNodeIndices;
nodeMeshBuilder->setDeformThickness(deformThickness);
nodeMeshBuilder->setDeformWidth(deformWidth); StrokeMeshBuilder *strokeMeshBuilder = new StrokeMeshBuilder;
nodeMeshBuilder->setDeformMapScale(deformMapScale);
nodeMeshBuilder->setHollowThickness(hollowThickness); strokeMeshBuilder->setDeformThickness(deformThickness);
strokeMeshBuilder->setDeformWidth(deformWidth);
strokeMeshBuilder->setDeformMapScale(deformMapScale);
strokeMeshBuilder->setHollowThickness(hollowThickness);
if (nullptr != deformImage) if (nullptr != deformImage)
nodeMeshBuilder->setDeformMapImage(deformImage); strokeMeshBuilder->setDeformMapImage(deformImage);
if (PartBase::YZ == base) { if (PartBase::YZ == base) {
nodeMeshBuilder->enableBaseNormalOnX(false); strokeMeshBuilder->enableBaseNormalOnX(false);
} else if (PartBase::Average == base) { } else if (PartBase::Average == base) {
nodeMeshBuilder->enableBaseNormalAverage(true); strokeMeshBuilder->enableBaseNormalAverage(true);
} else if (PartBase::XY == base) { } else if (PartBase::XY == base) {
nodeMeshBuilder->enableBaseNormalOnZ(false); strokeMeshBuilder->enableBaseNormalOnZ(false);
} else if (PartBase::ZX == base) { } else if (PartBase::ZX == base) {
nodeMeshBuilder->enableBaseNormalOnY(false); strokeMeshBuilder->enableBaseNormalOnY(false);
} }
std::vector<size_t> builderNodeIndices; for (const auto &node: strokeModifier->nodes()) {
for (const auto &node: nodeMeshModifier->nodes()) { auto nodeIndex = strokeMeshBuilder->addNode(node.position, node.radius, node.cutTemplate, node.cutRotation);
auto nodeIndex = nodeMeshBuilder->addNode(node.position, node.radius, node.cutTemplate, node.cutRotation); strokeMeshBuilder->setNodeOriginInfo(nodeIndex, node.nearOriginNodeIndex, node.farOriginNodeIndex);
nodeMeshBuilder->setNodeOriginInfo(nodeIndex, node.nearOriginNodeIndex, node.farOriginNodeIndex);
builderNodeIndices.push_back(nodeIndex);
const auto &originNodeIdString = nodeIndexToIdStringMap[node.originNodeIndex];
OutcomePaintNode paintNode;
paintNode.originNodeIndex = node.originNodeIndex;
paintNode.originNodeId = QUuid(originNodeIdString);
paintNode.radius = node.radius;
paintNode.origin = node.position;
partCache.outcomePaintMap.paintNodes.push_back(paintNode);
} }
for (const auto &edge: nodeMeshModifier->edges()) for (const auto &edge: strokeModifier->edges())
nodeMeshBuilder->addEdge(edge.firstNodeIndex, edge.secondNodeIndex); strokeMeshBuilder->addEdge(edge.firstNodeIndex, edge.secondNodeIndex);
buildSucceed = nodeMeshBuilder->build();
partCache.vertices = nodeMeshBuilder->generatedVertices(); if (fillMeshFileId.isNull()) {
partCache.faces = nodeMeshBuilder->generatedFaces(); for (const auto &nodeIt: nodeInfos) {
for (size_t i = 0; i < partCache.vertices.size(); ++i) { const auto &nodeIdString = nodeIt.first;
const auto &position = partCache.vertices[i]; const auto &nodeInfo = nodeIt.second;
const auto &source = nodeMeshBuilder->generatedVerticesSourceNodeIndices()[i]; addNodeToPartCache(nodeIdString, nodeInfo);
size_t nodeIndex = nodeMeshModifier->nodes()[source].originNodeIndex; }
const auto &nodeIdString = nodeIndexToIdStringMap[nodeIndex];
partCache.outcomeNodeVertices.push_back({position, {partIdString, nodeIdString}});
auto &paintNode = partCache.outcomePaintMap.paintNodes[source]; for (const auto &edgeIt: edges) {
paintNode.vertices.push_back(position); const QString &fromNodeIdString = edgeIt.first;
const QString &toNodeIdString = edgeIt.second;
addEdgeToPartCache(fromNodeIdString, toNodeIdString);
}
for (const auto &node: strokeModifier->nodes()) {
const auto &originNodeIdString = nodeIndexToIdStringMap[node.originNodeIndex];
OutcomePaintNode paintNode;
paintNode.originNodeIndex = node.originNodeIndex;
paintNode.originNodeId = QUuid(originNodeIdString);
paintNode.radius = node.radius;
paintNode.origin = node.position;
partCache.outcomePaintMap.paintNodes.push_back(paintNode);
}
buildSucceed = strokeMeshBuilder->build();
partCache.vertices = strokeMeshBuilder->generatedVertices();
partCache.faces = strokeMeshBuilder->generatedFaces();
sourceNodeIndices = strokeMeshBuilder->generatedVerticesSourceNodeIndices();
for (size_t i = 0; i < partCache.vertices.size(); ++i) {
const auto &position = partCache.vertices[i];
const auto &source = strokeMeshBuilder->generatedVerticesSourceNodeIndices()[i];
size_t nodeIndex = strokeModifier->nodes()[source].originNodeIndex;
const auto &nodeIdString = nodeIndexToIdStringMap[nodeIndex];
partCache.outcomeNodeVertices.push_back({position, {partIdString, nodeIdString}});
auto &paintNode = partCache.outcomePaintMap.paintNodes[source];
paintNode.vertices.push_back(position);
}
for (size_t i = 0; i < partCache.outcomePaintMap.paintNodes.size(); ++i) {
auto &paintNode = partCache.outcomePaintMap.paintNodes[i];
paintNode.baseNormal = strokeMeshBuilder->nodeBaseNormal(i);
paintNode.direction = strokeMeshBuilder->nodeTraverseDirection(i);
paintNode.order = strokeMeshBuilder->nodeTraverseOrder(i);
partCache.outcomeNodes[paintNode.originNodeIndex].direction = paintNode.direction;
}
} else {
if (strokeMeshBuilder->buildBaseNormalsOnly())
buildSucceed = fillPartWithMesh(partCache, fillMeshFileId, cutRotation, strokeMeshBuilder);
} }
for (size_t i = 0; i < partCache.outcomePaintMap.paintNodes.size(); ++i) { delete strokeMeshBuilder;
auto &paintNode = partCache.outcomePaintMap.paintNodes[i]; strokeMeshBuilder = nullptr;
paintNode.baseNormal = nodeMeshBuilder->nodeBaseNormal(i);
paintNode.direction = nodeMeshBuilder->nodeTraverseDirection(i);
paintNode.order = nodeMeshBuilder->nodeTraverseOrder(i);
partCache.outcomeNodes[paintNode.originNodeIndex].direction = paintNode.direction;
}
bool hasMeshError = false; bool hasMeshError = false;
MeshCombiner::Mesh *mesh = nullptr; MeshCombiner::Mesh *mesh = nullptr;
@ -667,8 +703,8 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString,
for (size_t i = 0; i < xMirroredVertices.size(); ++i) { for (size_t i = 0; i < xMirroredVertices.size(); ++i) {
const auto &position = xMirroredVertices[i]; const auto &position = xMirroredVertices[i];
size_t nodeIndex = 0; size_t nodeIndex = 0;
const auto &source = nodeMeshBuilder->generatedVerticesSourceNodeIndices()[i]; const auto &source = sourceNodeIndices[i];
nodeIndex = nodeMeshModifier->nodes()[source].originNodeIndex; nodeIndex = strokeModifier->nodes()[source].originNodeIndex;
const auto &nodeIdString = nodeIndexToIdStringMap[nodeIndex]; const auto &nodeIdString = nodeIndexToIdStringMap[nodeIndex];
partCache.outcomeNodeVertices.push_back({position, {mirroredPartIdString, nodeIdString}}); partCache.outcomeNodeVertices.push_back({position, {mirroredPartIdString, nodeIdString}});
} }
@ -716,14 +752,14 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString,
partCache.mesh = new MeshCombiner::Mesh(*mesh); partCache.mesh = new MeshCombiner::Mesh(*mesh);
mesh->fetch(partPreviewVertices, partCache.previewTriangles); mesh->fetch(partPreviewVertices, partCache.previewTriangles);
partCache.previewVertices = partPreviewVertices; partCache.previewVertices = partPreviewVertices;
partCache.isSucceed = true; partCache.isSuccessful = true;
} }
if (partCache.previewTriangles.empty()) { if (partCache.previewTriangles.empty()) {
partPreviewVertices = partCache.vertices; partPreviewVertices = partCache.vertices;
triangulateFacesWithoutKeepVertices(partPreviewVertices, partCache.faces, partCache.previewTriangles); triangulateFacesWithoutKeepVertices(partPreviewVertices, partCache.faces, partCache.previewTriangles);
partCache.previewVertices = partPreviewVertices; partCache.previewVertices = partPreviewVertices;
partPreviewColor = Qt::red; partPreviewColor = Qt::red;
partCache.isSucceed = false; partCache.isSuccessful = false;
} }
trim(&partPreviewVertices, true); trim(&partPreviewVertices, true);
@ -746,14 +782,13 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString,
if (!partCache.previewTriangles.empty()) { if (!partCache.previewTriangles.empty()) {
if (target == PartTarget::CutFace) if (target == PartTarget::CutFace)
partPreviewColor = Theme::red; partPreviewColor = Theme::red;
m_partPreviewMeshes[partId] = new MeshLoader(partPreviewVertices, m_partPreviewMeshes[partId] = new Model(partPreviewVertices,
partCache.previewTriangles, partCache.previewTriangles,
partPreviewTriangleVertexNormals, partPreviewTriangleVertexNormals,
partPreviewColor); partPreviewColor);
} }
delete nodeMeshBuilder; delete strokeModifier;
delete nodeMeshModifier;
if (mesh && mesh->isNull()) { if (mesh && mesh->isNull()) {
delete mesh; delete mesh;
@ -772,12 +807,79 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString,
if (hasMeshError && target == PartTarget::Model) { if (hasMeshError && target == PartTarget::Model) {
*hasError = true; *hasError = true;
//m_isSucceed = false;
} }
return mesh; return mesh;
} }
bool MeshGenerator::fillPartWithMesh(GeneratedPart &partCache,
const QUuid &fillMeshFileId,
float cutRotation,
const StrokeMeshBuilder *strokeMeshBuilder)
{
bool fillIsSucessful = false;
const QByteArray *fillMeshByteArray = FileForever::getContent(fillMeshFileId);
if (nullptr == fillMeshByteArray)
return false;
QXmlStreamReader fillMeshStream(*fillMeshByteArray);
Snapshot *fillMeshSnapshot = new Snapshot;
loadSkeletonFromXmlStream(fillMeshSnapshot, fillMeshStream);
GeneratedCacheContext *fillMeshCacheContext = new GeneratedCacheContext();
MeshGenerator *meshGenerator = new MeshGenerator(fillMeshSnapshot);
meshGenerator->setWeldEnabled(false);
meshGenerator->setGeneratedCacheContext(fillMeshCacheContext);
meshGenerator->generate();
fillIsSucessful = meshGenerator->isSuccessful();
Outcome *outcome = meshGenerator->takeOutcome();
if (nullptr != outcome) {
MeshStroketifier stroketifier;
std::vector<MeshStroketifier::Node> strokeNodes;
for (const auto &nodeIndex: strokeMeshBuilder->nodeIndices()) {
const auto &node = strokeMeshBuilder->nodes()[nodeIndex];
MeshStroketifier::Node strokeNode;
strokeNode.position = node.position;
strokeNode.radius = node.radius;
strokeNodes.push_back(strokeNode);
}
stroketifier.setCutRotation(cutRotation);
if (stroketifier.prepare(strokeNodes, outcome->vertices)) {
stroketifier.stroketify(&outcome->vertices);
std::vector<MeshStroketifier::Node> agentNodes(outcome->nodes.size());
for (size_t i = 0; i < outcome->nodes.size(); ++i) {
auto &dest = agentNodes[i];
const auto &src = outcome->nodes[i];
dest.position = src.origin;
dest.radius = src.radius;
}
stroketifier.stroketify(&agentNodes);
for (size_t i = 0; i < outcome->nodes.size(); ++i) {
const auto &src = agentNodes[i];
auto &dest = outcome->nodes[i];
dest.origin = src.position;
dest.radius = src.radius;
}
}
partCache.outcomeNodes.insert(partCache.outcomeNodes.end(), outcome->nodes.begin(), outcome->nodes.end());
partCache.outcomeEdges.insert(partCache.outcomeEdges.end(), outcome->edges.begin(), outcome->edges.end());
partCache.vertices.insert(partCache.vertices.end(), outcome->vertices.begin(), outcome->vertices.end());
if (!strokeNodes.empty()) {
for (auto &it: partCache.vertices)
it += strokeNodes.front().position;
}
for (size_t i = 0; i < outcome->vertexSourceNodes.size(); ++i)
partCache.outcomeNodeVertices.push_back({partCache.vertices[i], outcome->vertexSourceNodes[i]});
partCache.faces.insert(partCache.faces.end(), outcome->triangleAndQuads.begin(), outcome->triangleAndQuads.end());
fillIsSucessful = true;
}
delete outcome;
delete meshGenerator;
delete fillMeshCacheContext;
return fillIsSucessful;
}
const std::map<QString, QString> *MeshGenerator::findComponent(const QString &componentIdString) const std::map<QString, QString> *MeshGenerator::findComponent(const QString &componentIdString)
{ {
const std::map<QString, QString> *component = &m_snapshot->rootComponent; const std::map<QString, QString> *component = &m_snapshot->rootComponent;
@ -935,14 +1037,18 @@ MeshCombiner::Mesh *MeshGenerator::combineComponentMesh(const QString &component
if ("partId" == linkDataType) { if ("partId" == linkDataType) {
QString partIdString = valueOfKeyInMapOrEmpty(*component, "linkData"); QString partIdString = valueOfKeyInMapOrEmpty(*component, "linkData");
bool hasError = false; bool hasError = false;
mesh = combinePartMesh(partIdString, &hasError); bool retryable = true;
mesh = combinePartMesh(partIdString, &hasError, &retryable);
if (hasError) { if (hasError) {
delete mesh; delete mesh;
hasError = false; mesh = nullptr;
qDebug() << "Try combine part again without adding intermediate nodes"; if (retryable) {
mesh = combinePartMesh(partIdString, &hasError, false); hasError = false;
qDebug() << "Try combine part again without adding intermediate nodes";
mesh = combinePartMesh(partIdString, &hasError, &retryable, false);
}
if (hasError) { if (hasError) {
m_isSucceed = false; m_isSuccessful = false;
} }
} }
@ -1176,7 +1282,7 @@ MeshCombiner::Mesh *MeshGenerator::combineMultipleMeshes(const std::vector<std::
delete mesh; delete mesh;
mesh = newMesh; mesh = newMesh;
} else { } else {
m_isSucceed = false; m_isSuccessful = false;
qDebug() << "Mesh combine failed"; qDebug() << "Mesh combine failed";
delete newMesh; delete newMesh;
} }
@ -1318,7 +1424,7 @@ void MeshGenerator::generate()
if (nullptr == m_snapshot) if (nullptr == m_snapshot)
return; return;
m_isSucceed = true; m_isSuccessful = true;
QElapsedTimer countTimeConsumed; QElapsedTimer countTimeConsumed;
countTimeConsumed.start(); countTimeConsumed.start();
@ -1399,23 +1505,22 @@ void MeshGenerator::generate()
std::vector<std::vector<size_t>> combinedFaces; std::vector<std::vector<size_t>> combinedFaces;
if (nullptr != combinedMesh) { if (nullptr != combinedMesh) {
combinedMesh->fetch(combinedVertices, combinedFaces); combinedMesh->fetch(combinedVertices, combinedFaces);
if (m_weldEnabled) {
if (!remeshed) { if (!remeshed) {
size_t totalAffectedNum = 0; size_t totalAffectedNum = 0;
size_t affectedNum = 0; size_t affectedNum = 0;
do { do {
std::vector<QVector3D> weldedVertices; std::vector<QVector3D> weldedVertices;
std::vector<std::vector<size_t>> weldedFaces; std::vector<std::vector<size_t>> weldedFaces;
affectedNum = weldSeam(combinedVertices, combinedFaces, affectedNum = weldSeam(combinedVertices, combinedFaces,
0.025, componentCache.noneSeamVertices, 0.025, componentCache.noneSeamVertices,
weldedVertices, weldedFaces); weldedVertices, weldedFaces);
combinedVertices = weldedVertices; combinedVertices = weldedVertices;
combinedFaces = weldedFaces; combinedFaces = weldedFaces;
totalAffectedNum += affectedNum; totalAffectedNum += affectedNum;
} while (affectedNum > 0); } while (affectedNum > 0);
qDebug() << "Total weld affected triangles:" << totalAffectedNum; }
} }
m_outcome->nodes = componentCache.outcomeNodes; m_outcome->nodes = componentCache.outcomeNodes;
m_outcome->edges = componentCache.outcomeEdges; m_outcome->edges = componentCache.outcomeEdges;
m_outcome->paintMaps = componentCache.outcomePaintMaps; m_outcome->paintMaps = componentCache.outcomePaintMaps;
@ -1447,10 +1552,30 @@ void MeshGenerator::generate()
} }
collectClothComponent(QUuid().toString()); collectClothComponent(QUuid().toString());
collectErroredParts();
postprocessOutcome(m_outcome);
// Collect errored parts m_resultMesh = new Model(*m_outcome);
delete combinedMesh;
if (needDeleteCacheContext) {
delete m_cacheContext;
m_cacheContext = nullptr;
}
qDebug() << "The mesh generation took" << countTimeConsumed.elapsed() << "milliseconds";
}
void MeshGenerator::setWeldEnabled(bool enabled)
{
m_weldEnabled = enabled;
}
void MeshGenerator::collectErroredParts()
{
for (const auto &it: m_cacheContext->parts) { for (const auto &it: m_cacheContext->parts) {
if (!it.second.isSucceed) { if (!it.second.isSuccessful) {
if (!it.second.joined) if (!it.second.joined)
continue; continue;
@ -1472,56 +1597,44 @@ void MeshGenerator::generate()
m_outcome->triangles.insert(m_outcome->triangles.end(), errorTriangles.begin(), errorTriangles.end()); m_outcome->triangles.insert(m_outcome->triangles.end(), errorTriangles.begin(), errorTriangles.end());
} }
} }
}
auto postprocessOutcome = [this](Outcome *outcome) {
std::vector<QVector3D> combinedFacesNormals;
for (const auto &face: outcome->triangles) {
combinedFacesNormals.push_back(QVector3D::normal(
outcome->vertices[face[0]],
outcome->vertices[face[1]],
outcome->vertices[face[2]]
));
}
outcome->triangleNormals = combinedFacesNormals;
std::vector<std::pair<QUuid, QUuid>> sourceNodes;
triangleSourceNodeResolve(*outcome, sourceNodes, &outcome->vertexSourceNodes);
outcome->setTriangleSourceNodes(sourceNodes);
std::map<std::pair<QUuid, QUuid>, QColor> sourceNodeToColorMap;
for (const auto &node: outcome->nodes)
sourceNodeToColorMap.insert({{node.partId, node.nodeId}, node.color});
outcome->triangleColors.resize(outcome->triangles.size(), Qt::white);
const std::vector<std::pair<QUuid, QUuid>> *triangleSourceNodes = outcome->triangleSourceNodes();
if (nullptr != triangleSourceNodes) {
for (size_t triangleIndex = 0; triangleIndex < outcome->triangles.size(); triangleIndex++) {
const auto &source = (*triangleSourceNodes)[triangleIndex];
outcome->triangleColors[triangleIndex] = sourceNodeToColorMap[source];
}
}
std::vector<std::vector<QVector3D>> triangleVertexNormals;
generateSmoothTriangleVertexNormals(outcome->vertices,
outcome->triangles,
outcome->triangleNormals,
&triangleVertexNormals);
outcome->setTriangleVertexNormals(triangleVertexNormals);
};
postprocessOutcome(m_outcome);
m_resultMesh = new MeshLoader(*m_outcome);
delete combinedMesh;
if (needDeleteCacheContext) { void MeshGenerator::postprocessOutcome(Outcome *outcome)
delete m_cacheContext; {
m_cacheContext = nullptr; std::vector<QVector3D> combinedFacesNormals;
for (const auto &face: outcome->triangles) {
combinedFacesNormals.push_back(QVector3D::normal(
outcome->vertices[face[0]],
outcome->vertices[face[1]],
outcome->vertices[face[2]]
));
} }
qDebug() << "The mesh generation took" << countTimeConsumed.elapsed() << "milliseconds"; outcome->triangleNormals = combinedFacesNormals;
std::vector<std::pair<QUuid, QUuid>> sourceNodes;
triangleSourceNodeResolve(*outcome, sourceNodes, &outcome->vertexSourceNodes);
outcome->setTriangleSourceNodes(sourceNodes);
std::map<std::pair<QUuid, QUuid>, QColor> sourceNodeToColorMap;
for (const auto &node: outcome->nodes)
sourceNodeToColorMap.insert({{node.partId, node.nodeId}, node.color});
outcome->triangleColors.resize(outcome->triangles.size(), Qt::white);
const std::vector<std::pair<QUuid, QUuid>> *triangleSourceNodes = outcome->triangleSourceNodes();
if (nullptr != triangleSourceNodes) {
for (size_t triangleIndex = 0; triangleIndex < outcome->triangles.size(); triangleIndex++) {
const auto &source = (*triangleSourceNodes)[triangleIndex];
outcome->triangleColors[triangleIndex] = sourceNodeToColorMap[source];
}
}
std::vector<std::vector<QVector3D>> triangleVertexNormals;
generateSmoothTriangleVertexNormals(outcome->vertices,
outcome->triangles,
outcome->triangleNormals,
&triangleVertexNormals);
outcome->setTriangleVertexNormals(triangleVertexNormals);
} }
void MeshGenerator::remesh(const std::vector<OutcomeNode> &inputNodes, void MeshGenerator::remesh(const std::vector<OutcomeNode> &inputNodes,

View File

@ -10,9 +10,10 @@
#include "outcome.h" #include "outcome.h"
#include "snapshot.h" #include "snapshot.h"
#include "combinemode.h" #include "combinemode.h"
#include "meshloader.h" #include "model.h"
#include "componentlayer.h" #include "componentlayer.h"
#include "clothforce.h" #include "clothforce.h"
#include "strokemodifier.h"
class GeneratedPart class GeneratedPart
{ {
@ -30,7 +31,7 @@ public:
std::vector<QVector3D> previewVertices; std::vector<QVector3D> previewVertices;
std::vector<std::vector<size_t>> previewTriangles; std::vector<std::vector<size_t>> previewTriangles;
OutcomePaintMap outcomePaintMap; OutcomePaintMap outcomePaintMap;
bool isSucceed = false; bool isSuccessful = false;
bool joined = true; bool joined = true;
}; };
@ -65,9 +66,9 @@ class MeshGenerator : public QObject
public: public:
MeshGenerator(Snapshot *snapshot); MeshGenerator(Snapshot *snapshot);
~MeshGenerator(); ~MeshGenerator();
bool isSucceed(); bool isSuccessful();
MeshLoader *takeResultMesh(); Model *takeResultMesh();
MeshLoader *takePartPreviewMesh(const QUuid &partId); Model *takePartPreviewMesh(const QUuid &partId);
const std::set<QUuid> &generatedPreviewPartIds(); const std::set<QUuid> &generatedPreviewPartIds();
Outcome *takeOutcome(); Outcome *takeOutcome();
std::map<QUuid, StrokeMeshBuilder::CutFaceTransform> *takeCutFaceTransforms(); std::map<QUuid, StrokeMeshBuilder::CutFaceTransform> *takeCutFaceTransforms();
@ -77,6 +78,7 @@ public:
void setSmoothShadingThresholdAngleDegrees(float degrees); void setSmoothShadingThresholdAngleDegrees(float degrees);
void setDefaultPartColor(const QColor &color); void setDefaultPartColor(const QColor &color);
void setId(quint64 id); void setId(quint64 id);
void setWeldEnabled(bool enabled);
quint64 id(); quint64 id();
signals: signals:
void finished(); void finished();
@ -96,9 +98,9 @@ private:
std::map<QString, std::set<QString>> m_partNodeIds; std::map<QString, std::set<QString>> m_partNodeIds;
std::map<QString, std::set<QString>> m_partEdgeIds; std::map<QString, std::set<QString>> m_partEdgeIds;
std::set<QUuid> m_generatedPreviewPartIds; std::set<QUuid> m_generatedPreviewPartIds;
MeshLoader *m_resultMesh = nullptr; Model *m_resultMesh = nullptr;
std::map<QUuid, MeshLoader *> m_partPreviewMeshes; std::map<QUuid, Model *> m_partPreviewMeshes;
bool m_isSucceed = false; bool m_isSuccessful = false;
bool m_cacheEnabled = false; bool m_cacheEnabled = false;
float m_smoothShadingThresholdAngleDegrees = 60; float m_smoothShadingThresholdAngleDegrees = 60;
std::map<QUuid, StrokeMeshBuilder::CutFaceTransform> *m_cutFaceTransforms = nullptr; std::map<QUuid, StrokeMeshBuilder::CutFaceTransform> *m_cutFaceTransforms = nullptr;
@ -106,13 +108,18 @@ private:
quint64 m_id = 0; quint64 m_id = 0;
std::vector<QVector3D> m_clothCollisionVertices; std::vector<QVector3D> m_clothCollisionVertices;
std::vector<std::vector<size_t>> m_clothCollisionTriangles; std::vector<std::vector<size_t>> m_clothCollisionTriangles;
bool m_weldEnabled = true;
void collectParts(); void collectParts();
bool checkIsComponentDirty(const QString &componentIdString); bool checkIsComponentDirty(const QString &componentIdString);
bool checkIsPartDirty(const QString &partIdString); bool checkIsPartDirty(const QString &partIdString);
bool checkIsPartDependencyDirty(const QString &partIdString); bool checkIsPartDependencyDirty(const QString &partIdString);
void checkDirtyFlags(); void checkDirtyFlags();
MeshCombiner::Mesh *combinePartMesh(const QString &partIdString, bool *hasError, bool addIntermediateNodes=true); bool fillPartWithMesh(GeneratedPart &partCache,
const QUuid &fillMeshFileId,
float cutRotation,
const StrokeMeshBuilder *strokeMeshBuilder);
MeshCombiner::Mesh *combinePartMesh(const QString &partIdString, bool *hasError, bool *retryable, bool addIntermediateNodes=true);
MeshCombiner::Mesh *combineComponentMesh(const QString &componentIdString, CombineMode *combineMode); MeshCombiner::Mesh *combineComponentMesh(const QString &componentIdString, CombineMode *combineMode);
void makeXmirror(const std::vector<QVector3D> &sourceVertices, const std::vector<std::vector<size_t>> &sourceFaces, void makeXmirror(const std::vector<QVector3D> &sourceVertices, const std::vector<std::vector<size_t>> &sourceFaces,
std::vector<QVector3D> *destVertices, std::vector<std::vector<size_t>> *destFaces); std::vector<QVector3D> *destVertices, std::vector<std::vector<size_t>> *destFaces);
@ -150,6 +157,8 @@ private:
std::vector<std::vector<size_t>> *outputQuads, std::vector<std::vector<size_t>> *outputQuads,
std::vector<std::vector<size_t>> *outputTriangles, std::vector<std::vector<size_t>> *outputTriangles,
std::vector<std::pair<QVector3D, std::pair<QUuid, QUuid>>> *outputNodeVertices); std::vector<std::pair<QVector3D, std::pair<QUuid, QUuid>>> *outputNodeVertices);
void postprocessOutcome(Outcome *outcome);
void collectErroredParts();
}; };
#endif #endif

View File

@ -97,7 +97,7 @@ size_t MeshRecombiner::splitSeamVerticesToIslands(const std::map<size_t, std::ve
bool MeshRecombiner::buildHalfEdgeToFaceMap(std::map<std::pair<size_t, size_t>, size_t> &halfEdgeToFaceMap) bool MeshRecombiner::buildHalfEdgeToFaceMap(std::map<std::pair<size_t, size_t>, size_t> &halfEdgeToFaceMap)
{ {
bool succeed = true; bool isSuccessful = true;
for (size_t faceIndex = 0; faceIndex < m_faces->size(); ++faceIndex) { for (size_t faceIndex = 0; faceIndex < m_faces->size(); ++faceIndex) {
const auto &face = (*m_faces)[faceIndex]; const auto &face = (*m_faces)[faceIndex];
for (size_t i = 0; i < face.size(); ++i) { for (size_t i = 0; i < face.size(); ++i) {
@ -105,11 +105,11 @@ bool MeshRecombiner::buildHalfEdgeToFaceMap(std::map<std::pair<size_t, size_t>,
const auto insertResult = halfEdgeToFaceMap.insert({{face[i], face[j]}, faceIndex}); const auto insertResult = halfEdgeToFaceMap.insert({{face[i], face[j]}, faceIndex});
if (!insertResult.second) { if (!insertResult.second) {
//qDebug() << "Non manifold edge found:" << face[i] << face[j]; //qDebug() << "Non manifold edge found:" << face[i] << face[j];
succeed = false; isSuccessful = false;
} }
} }
} }
return succeed; return isSuccessful;
} }
bool MeshRecombiner::recombine() bool MeshRecombiner::recombine()

198
src/meshstroketifier.cpp Normal file
View File

@ -0,0 +1,198 @@
#include <QMatrix4x4>
#include <QQuaternion>
#include <set>
#include "meshstroketifier.h"
void MeshStroketifier::setCutRotation(float cutRotation)
{
m_cutRotation = cutRotation;
}
bool MeshStroketifier::prepare(const std::vector<Node> &strokeNodes,
const std::vector<QVector3D> &vertices)
{
if (strokeNodes.empty() || vertices.empty())
return false;
float boundingBoxMinX = 0.0;
float boundingBoxMaxX = 0.0;
float boundingBoxMinY = 0.0;
float boundingBoxMaxY = 0.0;
float boundingBoxMinZ = 0.0;
float boundingBoxMaxZ = 0.0;
calculateBoundingBox(vertices,
&boundingBoxMinX, &boundingBoxMaxX,
&boundingBoxMinY, &boundingBoxMaxY,
&boundingBoxMinZ, &boundingBoxMaxZ);
float xLength = boundingBoxMaxX - boundingBoxMinX;
float yLength = boundingBoxMaxY - boundingBoxMinY;
float zLength = boundingBoxMaxZ - boundingBoxMinZ;
if (yLength >= xLength && yLength >= zLength) {
// Y-axis
m_modelOrigin = QVector3D((boundingBoxMinX + boundingBoxMaxX) * 0.5,
boundingBoxMinY,
(boundingBoxMinZ + boundingBoxMaxZ) * 0.5);
m_modelAlignDirection = QVector3D(0.0, 1.0, 0.0);
m_modelLength = yLength;
} else if (zLength >= xLength && zLength >= yLength) {
// Z-axis
m_modelOrigin = QVector3D((boundingBoxMinX + boundingBoxMaxX) * 0.5,
(boundingBoxMinY + boundingBoxMaxY) * 0.5,
boundingBoxMinZ);
m_modelAlignDirection = QVector3D(0.0, 0.0, 1.0);
m_modelLength = zLength;
} else {
// X-axis
m_modelOrigin = QVector3D(boundingBoxMinX,
(boundingBoxMinY + boundingBoxMaxY) * 0.5,
(boundingBoxMinZ + boundingBoxMaxZ) * 0.5);
m_modelAlignDirection = QVector3D(1.0, 0.0, 0.0);
m_modelLength = xLength;
}
std::vector<float> strokeSegmentLengths;
float strokeLength = calculateStrokeLengths(strokeNodes, &strokeSegmentLengths);
if (strokeLength > 0)
m_scaleAmount = strokeLength / (m_modelLength + std::numeric_limits<float>::epsilon());
if (!strokeSegmentLengths.empty()) {
float offset = 0;
for (size_t i = 0; i < strokeSegmentLengths.size(); ++i) {
offset += strokeSegmentLengths[i];
m_modelJointPositions.push_back(m_modelAlignDirection * offset);
}
QMatrix4x4 matrix;
if (!qFuzzyIsNull(m_cutRotation)) {
QMatrix4x4 cutRotationMatrix;
cutRotationMatrix.rotate(m_cutRotation * 180, m_modelAlignDirection);
matrix *= cutRotationMatrix;
}
QVector3D rotateFromDirection = m_modelAlignDirection;
for (size_t i = 1; i < strokeNodes.size(); ++i) {
size_t h = i - 1;
auto newDirection = (strokeNodes[i].position - strokeNodes[h].position).normalized();
auto rotation = QQuaternion::rotationTo(rotateFromDirection, newDirection);
rotateFromDirection = newDirection;
QMatrix4x4 rotationMatrix;
rotationMatrix.rotate(rotation);
matrix *= rotationMatrix;
m_modelTransforms.push_back(matrix);
}
}
return true;
}
void MeshStroketifier::stroketify(std::vector<QVector3D> *vertices)
{
translate(vertices);
scale(vertices);
deform(vertices);
}
void MeshStroketifier::stroketify(std::vector<Node> *nodes)
{
std::vector<QVector3D> positions(nodes->size());
for (size_t i = 0; i < nodes->size(); ++i)
positions[i] = (*nodes)[i].position;
stroketify(&positions);
for (size_t i = 0; i < nodes->size(); ++i) {
auto &node = (*nodes)[i];
node.position = positions[i];
node.radius *= m_scaleAmount;
}
}
float MeshStroketifier::calculateStrokeLengths(const std::vector<Node> &strokeNodes,
std::vector<float> *lengths)
{
float total = 0.0;
for (size_t i = 1; i < strokeNodes.size(); ++i) {
size_t h = i - 1;
const auto &strokeNodeH = strokeNodes[h];
const auto &strokeNodeI = strokeNodes[i];
float distance = (strokeNodeH.position - strokeNodeI.position).length();
total += distance;
if (nullptr != lengths)
lengths->push_back(distance);
}
return total;
}
void MeshStroketifier::calculateBoundingBox(const std::vector<QVector3D> &vertices,
float *minX, float *maxX,
float *minY, float *maxY,
float *minZ, float *maxZ)
{
*minX = *minY = *minZ = std::numeric_limits<float>::max();
*maxX = *maxY = *maxZ = std::numeric_limits<float>::lowest();
for (const auto &it: vertices) {
if (it.x() < *minX)
*minX = it.x();
if (it.x() > *maxX)
*maxX = it.x();
if (it.y() < *minY)
*minY = it.y();
if (it.y() > *maxY)
*maxY = it.y();
if (it.z() < *minZ)
*minZ = it.z();
if (it.z() > *maxZ)
*maxZ = it.z();
}
}
void MeshStroketifier::translate(std::vector<QVector3D> *vertices)
{
for (auto &it: *vertices)
it += -m_modelOrigin;
}
void MeshStroketifier::scale(std::vector<QVector3D> *vertices)
{
for (auto &it: *vertices)
it *= m_scaleAmount;
}
void MeshStroketifier::deform(std::vector<QVector3D> *vertices)
{
if (m_modelJointPositions.empty() ||
m_modelTransforms.empty() ||
m_modelJointPositions.size() != m_modelTransforms.size()) {
return;
}
std::set<size_t> remaining;
for (size_t i = 0; i < vertices->size(); ++i)
remaining.insert(i);
std::vector<std::vector<size_t>> skinnedIndices;
for (size_t i = 0; i < m_modelJointPositions.size(); ++i) {
std::vector<size_t> indices;
for (const auto &index: remaining) {
if (QVector3D::dotProduct(((*vertices)[index] - m_modelJointPositions[i]).normalized(),
m_modelAlignDirection) < 0) {
indices.push_back(index);
}
}
for (const auto &index: indices)
remaining.erase(index);
skinnedIndices.push_back(indices);
}
if (!remaining.empty())
skinnedIndices.back().insert(skinnedIndices.back().end(), remaining.begin(), remaining.end());
for (size_t i = 0; i < skinnedIndices.size() && i < m_modelTransforms.size(); ++i) {
const auto &matrix = m_modelTransforms[i];
for (const auto &index: skinnedIndices[i])
(*vertices)[index] = matrix * (*vertices)[index];
}
}

44
src/meshstroketifier.h Normal file
View File

@ -0,0 +1,44 @@
#ifndef DUST3D_MESH_STROKETIFIER_H
#define DUST3D_MESH_STROKETIFIER_H
#include <QObject>
#include <QVector3D>
#include <QMatrix4x4>
#include <vector>
class MeshStroketifier : public QObject
{
Q_OBJECT
public:
struct Node
{
QVector3D position;
float radius;
};
void setCutRotation(float cutRotation);
bool prepare(const std::vector<Node> &strokeNodes,
const std::vector<QVector3D> &vertices);
void stroketify(std::vector<QVector3D> *vertices);
void stroketify(std::vector<Node> *nodes);
private:
float m_cutRotation = 0.0;
QVector3D m_modelOrigin;
float m_modelLength = 0.0;
float m_scaleAmount = 1.0;
QVector3D m_modelAlignDirection;
std::vector<QVector3D> m_modelJointPositions;
std::vector<QMatrix4x4> m_modelTransforms;
static float calculateStrokeLengths(const std::vector<Node> &strokeNodes,
std::vector<float> *lengths);
static void calculateBoundingBox(const std::vector<QVector3D> &vertices,
float *minX, float *maxX,
float *minY, float *maxY,
float *minZ, float *maxZ);
void translate(std::vector<QVector3D> *vertices);
void scale(std::vector<QVector3D> *vertices);
void deform(std::vector<QVector3D> *vertices);
};
#endif

View File

@ -2,15 +2,15 @@
#include <QTextStream> #include <QTextStream>
#include <QFile> #include <QFile>
#include <cmath> #include <cmath>
#include "meshloader.h" #include "model.h"
#include "version.h" #include "version.h"
#define MAX_VERTICES_PER_FACE 100 #define MAX_VERTICES_PER_FACE 100
float MeshLoader::m_defaultMetalness = 0.0; float Model::m_defaultMetalness = 0.0;
float MeshLoader::m_defaultRoughness = 1.0; float Model::m_defaultRoughness = 1.0;
MeshLoader::MeshLoader(const MeshLoader &mesh) : Model::Model(const Model &mesh) :
m_triangleVertices(nullptr), m_triangleVertices(nullptr),
m_triangleVertexCount(0), m_triangleVertexCount(0),
m_edgeVertices(nullptr), m_edgeVertices(nullptr),
@ -57,7 +57,7 @@ MeshLoader::MeshLoader(const MeshLoader &mesh) :
this->m_meshId = mesh.meshId(); this->m_meshId = mesh.meshId();
} }
void MeshLoader::removeColor() void Model::removeColor()
{ {
delete this->m_textureImage; delete this->m_textureImage;
this->m_textureImage = nullptr; this->m_textureImage = nullptr;
@ -80,7 +80,7 @@ void MeshLoader::removeColor()
} }
} }
MeshLoader::MeshLoader(ShaderVertex *triangleVertices, int vertexNum, ShaderVertex *edgeVertices, int edgeVertexCount) : Model::Model(ShaderVertex *triangleVertices, int vertexNum, ShaderVertex *edgeVertices, int edgeVertexCount) :
m_triangleVertices(triangleVertices), m_triangleVertices(triangleVertices),
m_triangleVertexCount(vertexNum), m_triangleVertexCount(vertexNum),
m_edgeVertices(edgeVertices), m_edgeVertices(edgeVertices),
@ -89,7 +89,7 @@ MeshLoader::MeshLoader(ShaderVertex *triangleVertices, int vertexNum, ShaderVert
{ {
} }
MeshLoader::MeshLoader(const std::vector<QVector3D> &vertices, const std::vector<std::vector<size_t>> &triangles, Model::Model(const std::vector<QVector3D> &vertices, const std::vector<std::vector<size_t>> &triangles,
const std::vector<std::vector<QVector3D>> &triangleVertexNormals, const std::vector<std::vector<QVector3D>> &triangleVertexNormals,
const QColor &color) const QColor &color)
{ {
@ -124,7 +124,7 @@ MeshLoader::MeshLoader(const std::vector<QVector3D> &vertices, const std::vector
} }
} }
MeshLoader::MeshLoader(Outcome &outcome) : Model::Model(Outcome &outcome) :
m_triangleVertices(nullptr), m_triangleVertices(nullptr),
m_triangleVertexCount(0), m_triangleVertexCount(0),
m_edgeVertices(nullptr), m_edgeVertices(nullptr),
@ -210,7 +210,7 @@ MeshLoader::MeshLoader(Outcome &outcome) :
} }
} }
MeshLoader::MeshLoader() : Model::Model() :
m_triangleVertices(nullptr), m_triangleVertices(nullptr),
m_triangleVertexCount(0), m_triangleVertexCount(0),
m_edgeVertices(nullptr), m_edgeVertices(nullptr),
@ -219,7 +219,7 @@ MeshLoader::MeshLoader() :
{ {
} }
MeshLoader::~MeshLoader() Model::~Model()
{ {
delete[] m_triangleVertices; delete[] m_triangleVertices;
m_triangleVertexCount = 0; m_triangleVertexCount = 0;
@ -232,117 +232,117 @@ MeshLoader::~MeshLoader()
delete m_metalnessRoughnessAmbientOcclusionImage; delete m_metalnessRoughnessAmbientOcclusionImage;
} }
const std::vector<QVector3D> &MeshLoader::vertices() const std::vector<QVector3D> &Model::vertices()
{ {
return m_vertices; return m_vertices;
} }
const std::vector<std::vector<size_t>> &MeshLoader::faces() const std::vector<std::vector<size_t>> &Model::faces()
{ {
return m_faces; return m_faces;
} }
const std::vector<QVector3D> &MeshLoader::triangulatedVertices() const std::vector<QVector3D> &Model::triangulatedVertices()
{ {
return m_triangulatedVertices; return m_triangulatedVertices;
} }
const std::vector<TriangulatedFace> &MeshLoader::triangulatedFaces() const std::vector<TriangulatedFace> &Model::triangulatedFaces()
{ {
return m_triangulatedFaces; return m_triangulatedFaces;
} }
ShaderVertex *MeshLoader::triangleVertices() ShaderVertex *Model::triangleVertices()
{ {
return m_triangleVertices; return m_triangleVertices;
} }
int MeshLoader::triangleVertexCount() int Model::triangleVertexCount()
{ {
return m_triangleVertexCount; return m_triangleVertexCount;
} }
ShaderVertex *MeshLoader::edgeVertices() ShaderVertex *Model::edgeVertices()
{ {
return m_edgeVertices; return m_edgeVertices;
} }
int MeshLoader::edgeVertexCount() int Model::edgeVertexCount()
{ {
return m_edgeVertexCount; return m_edgeVertexCount;
} }
ShaderVertex *MeshLoader::toolVertices() ShaderVertex *Model::toolVertices()
{ {
return m_toolVertices; return m_toolVertices;
} }
int MeshLoader::toolVertexCount() int Model::toolVertexCount()
{ {
return m_toolVertexCount; return m_toolVertexCount;
} }
void MeshLoader::setTextureImage(QImage *textureImage) void Model::setTextureImage(QImage *textureImage)
{ {
m_textureImage = textureImage; m_textureImage = textureImage;
} }
const QImage *MeshLoader::textureImage() const QImage *Model::textureImage()
{ {
return m_textureImage; return m_textureImage;
} }
void MeshLoader::setNormalMapImage(QImage *normalMapImage) void Model::setNormalMapImage(QImage *normalMapImage)
{ {
m_normalMapImage = normalMapImage; m_normalMapImage = normalMapImage;
} }
const QImage *MeshLoader::normalMapImage() const QImage *Model::normalMapImage()
{ {
return m_normalMapImage; return m_normalMapImage;
} }
const QImage *MeshLoader::metalnessRoughnessAmbientOcclusionImage() const QImage *Model::metalnessRoughnessAmbientOcclusionImage()
{ {
return m_metalnessRoughnessAmbientOcclusionImage; return m_metalnessRoughnessAmbientOcclusionImage;
} }
void MeshLoader::setMetalnessRoughnessAmbientOcclusionImage(QImage *image) void Model::setMetalnessRoughnessAmbientOcclusionImage(QImage *image)
{ {
m_metalnessRoughnessAmbientOcclusionImage = image; m_metalnessRoughnessAmbientOcclusionImage = image;
} }
bool MeshLoader::hasMetalnessInImage() bool Model::hasMetalnessInImage()
{ {
return m_hasMetalnessInImage; return m_hasMetalnessInImage;
} }
void MeshLoader::setHasMetalnessInImage(bool hasInImage) void Model::setHasMetalnessInImage(bool hasInImage)
{ {
m_hasMetalnessInImage = hasInImage; m_hasMetalnessInImage = hasInImage;
} }
bool MeshLoader::hasRoughnessInImage() bool Model::hasRoughnessInImage()
{ {
return m_hasRoughnessInImage; return m_hasRoughnessInImage;
} }
void MeshLoader::setHasRoughnessInImage(bool hasInImage) void Model::setHasRoughnessInImage(bool hasInImage)
{ {
m_hasRoughnessInImage = hasInImage; m_hasRoughnessInImage = hasInImage;
} }
bool MeshLoader::hasAmbientOcclusionInImage() bool Model::hasAmbientOcclusionInImage()
{ {
return m_hasAmbientOcclusionInImage; return m_hasAmbientOcclusionInImage;
} }
void MeshLoader::setHasAmbientOcclusionInImage(bool hasInImage) void Model::setHasAmbientOcclusionInImage(bool hasInImage)
{ {
m_hasAmbientOcclusionInImage = hasInImage; m_hasAmbientOcclusionInImage = hasInImage;
} }
void MeshLoader::exportAsObj(QTextStream *textStream) void Model::exportAsObj(QTextStream *textStream)
{ {
auto &stream = *textStream; auto &stream = *textStream;
stream << "# " << APP_NAME << " " << APP_HUMAN_VER << endl; stream << "# " << APP_NAME << " " << APP_HUMAN_VER << endl;
@ -359,7 +359,7 @@ void MeshLoader::exportAsObj(QTextStream *textStream)
} }
} }
void MeshLoader::exportAsObj(const QString &filename) void Model::exportAsObj(const QString &filename)
{ {
QFile file(filename); QFile file(filename);
if (file.open(QIODevice::WriteOnly)) { if (file.open(QIODevice::WriteOnly)) {
@ -368,7 +368,7 @@ void MeshLoader::exportAsObj(const QString &filename)
} }
} }
void MeshLoader::updateTool(ShaderVertex *toolVertices, int vertexNum) void Model::updateTool(ShaderVertex *toolVertices, int vertexNum)
{ {
delete[] m_toolVertices; delete[] m_toolVertices;
m_toolVertices = nullptr; m_toolVertices = nullptr;
@ -378,7 +378,7 @@ void MeshLoader::updateTool(ShaderVertex *toolVertices, int vertexNum)
m_toolVertexCount = vertexNum; m_toolVertexCount = vertexNum;
} }
void MeshLoader::updateEdges(ShaderVertex *edgeVertices, int edgeVertexCount) void Model::updateEdges(ShaderVertex *edgeVertices, int edgeVertexCount)
{ {
delete[] m_edgeVertices; delete[] m_edgeVertices;
m_edgeVertices = nullptr; m_edgeVertices = nullptr;
@ -388,7 +388,7 @@ void MeshLoader::updateEdges(ShaderVertex *edgeVertices, int edgeVertexCount)
m_edgeVertexCount = edgeVertexCount; m_edgeVertexCount = edgeVertexCount;
} }
void MeshLoader::updateTriangleVertices(ShaderVertex *triangleVertices, int triangleVertexCount) void Model::updateTriangleVertices(ShaderVertex *triangleVertices, int triangleVertexCount)
{ {
delete[] m_triangleVertices; delete[] m_triangleVertices;
m_triangleVertices = 0; m_triangleVertices = 0;
@ -398,12 +398,12 @@ void MeshLoader::updateTriangleVertices(ShaderVertex *triangleVertices, int tria
m_triangleVertexCount = triangleVertexCount; m_triangleVertexCount = triangleVertexCount;
} }
quint64 MeshLoader::meshId() const quint64 Model::meshId() const
{ {
return m_meshId; return m_meshId;
} }
void MeshLoader::setMeshId(quint64 id) void Model::setMeshId(quint64 id)
{ {
m_meshId = id; m_meshId = id;
} }

View File

@ -1,5 +1,5 @@
#ifndef DUST3D_MESH_LOADER_H #ifndef DUST3D_MODEL_H
#define DUST3D_MESH_LOADER_H #define DUST3D_MODEL_H
#include <QObject> #include <QObject>
#include <vector> #include <vector>
#include <QVector3D> #include <QVector3D>
@ -15,17 +15,17 @@ struct TriangulatedFace
QColor color; QColor color;
}; };
class MeshLoader class Model
{ {
public: public:
MeshLoader(const std::vector<QVector3D> &vertices, const std::vector<std::vector<size_t>> &triangles, Model(const std::vector<QVector3D> &vertices, const std::vector<std::vector<size_t>> &triangles,
const std::vector<std::vector<QVector3D>> &triangleVertexNormals, const std::vector<std::vector<QVector3D>> &triangleVertexNormals,
const QColor &color=Qt::white); const QColor &color=Qt::white);
MeshLoader(Outcome &outcome); Model(Outcome &outcome);
MeshLoader(ShaderVertex *triangleVertices, int vertexNum, ShaderVertex *edgeVertices=nullptr, int edgeVertexCount=0); Model(ShaderVertex *triangleVertices, int vertexNum, ShaderVertex *edgeVertices=nullptr, int edgeVertexCount=0);
MeshLoader(const MeshLoader &mesh); Model(const Model &mesh);
MeshLoader(); Model();
~MeshLoader(); ~Model();
ShaderVertex *triangleVertices(); ShaderVertex *triangleVertices();
int triangleVertexCount(); int triangleVertexCount();
ShaderVertex *edgeVertices(); ShaderVertex *edgeVertices();

View File

@ -31,7 +31,7 @@ ModelMeshBinder::~ModelMeshBinder()
delete m_currentToonDepthMap; delete m_currentToonDepthMap;
} }
void ModelMeshBinder::updateMesh(MeshLoader *mesh) void ModelMeshBinder::updateMesh(Model *mesh)
{ {
QMutexLocker lock(&m_newMeshMutex); QMutexLocker lock(&m_newMeshMutex);
if (mesh != m_mesh) { if (mesh != m_mesh) {
@ -43,12 +43,12 @@ void ModelMeshBinder::updateMesh(MeshLoader *mesh)
void ModelMeshBinder::reloadMesh() void ModelMeshBinder::reloadMesh()
{ {
MeshLoader *mesh = nullptr; Model *mesh = nullptr;
{ {
QMutexLocker lock(&m_newMeshMutex); QMutexLocker lock(&m_newMeshMutex);
if (nullptr == m_mesh) if (nullptr == m_mesh)
return; return;
mesh = new MeshLoader(*m_mesh); mesh = new Model(*m_mesh);
} }
if (nullptr != mesh) if (nullptr != mesh)
updateMesh(mesh); updateMesh(mesh);
@ -67,17 +67,17 @@ void ModelMeshBinder::enableEnvironmentLight()
m_environmentLightEnabled = true; m_environmentLightEnabled = true;
} }
MeshLoader *ModelMeshBinder::fetchCurrentMesh() Model *ModelMeshBinder::fetchCurrentMesh()
{ {
QMutexLocker lock(&m_meshMutex); QMutexLocker lock(&m_meshMutex);
if (nullptr == m_mesh) if (nullptr == m_mesh)
return nullptr; return nullptr;
return new MeshLoader(*m_mesh); return new Model(*m_mesh);
} }
void ModelMeshBinder::paint(ModelShaderProgram *program) void ModelMeshBinder::paint(ModelShaderProgram *program)
{ {
MeshLoader *newMesh = nullptr; Model *newMesh = nullptr;
bool hasNewMesh = false; bool hasNewMesh = false;
if (m_newMeshComing) { if (m_newMeshComing) {
QMutexLocker lock(&m_newMeshMutex); QMutexLocker lock(&m_newMeshMutex);

View File

@ -5,7 +5,7 @@
#include <QOpenGLBuffer> #include <QOpenGLBuffer>
#include <QString> #include <QString>
#include <QOpenGLTexture> #include <QOpenGLTexture>
#include "meshloader.h" #include "model.h"
#include "modelshaderprogram.h" #include "modelshaderprogram.h"
class ModelMeshBinder class ModelMeshBinder
@ -13,8 +13,8 @@ class ModelMeshBinder
public: public:
ModelMeshBinder(bool toolEnabled=false); ModelMeshBinder(bool toolEnabled=false);
~ModelMeshBinder(); ~ModelMeshBinder();
MeshLoader *fetchCurrentMesh(); Model *fetchCurrentMesh();
void updateMesh(MeshLoader *mesh); void updateMesh(Model *mesh);
void initialize(); void initialize();
void paint(ModelShaderProgram *program); void paint(ModelShaderProgram *program);
void cleanup(); void cleanup();
@ -29,8 +29,8 @@ public:
void fetchCurrentToonNormalAndDepthMaps(QImage *normalMap, QImage *depthMap); void fetchCurrentToonNormalAndDepthMaps(QImage *normalMap, QImage *depthMap);
void updateToonNormalAndDepthMaps(QImage *normalMap, QImage *depthMap); void updateToonNormalAndDepthMaps(QImage *normalMap, QImage *depthMap);
private: private:
MeshLoader *m_mesh = nullptr; Model *m_mesh = nullptr;
MeshLoader *m_newMesh = nullptr; Model *m_newMesh = nullptr;
int m_renderTriangleVertexCount = 0; int m_renderTriangleVertexCount = 0;
int m_renderEdgeVertexCount = 0; int m_renderEdgeVertexCount = 0;
int m_renderToolVertexCount = 0; int m_renderToolVertexCount = 0;

View File

@ -1,9 +1,9 @@
#include <QOpenGLFramebufferObjectFormat> #include <QOpenGLFramebufferObjectFormat>
#include <QThread> #include <QThread>
#include <QDebug> #include <QDebug>
#include "modelofflinerender.h" #include "modeloffscreenrender.h"
ModelOfflineRender::ModelOfflineRender(const QSurfaceFormat &format, QScreen *targetScreen) : ModelOffscreenRender::ModelOffscreenRender(const QSurfaceFormat &format, QScreen *targetScreen) :
QOffscreenSurface(targetScreen), QOffscreenSurface(targetScreen),
m_context(nullptr), m_context(nullptr),
m_mesh(nullptr) m_mesh(nullptr)
@ -11,10 +11,10 @@ ModelOfflineRender::ModelOfflineRender(const QSurfaceFormat &format, QScreen *ta
setFormat(format); setFormat(format);
create(); create();
if (!isValid()) if (!isValid())
qDebug() << "ModelOfflineRender is invalid"; qDebug() << "ModelOffscreenRender is invalid";
} }
ModelOfflineRender::~ModelOfflineRender() ModelOffscreenRender::~ModelOffscreenRender()
{ {
destroy(); destroy();
delete m_mesh; delete m_mesh;
@ -22,43 +22,43 @@ ModelOfflineRender::~ModelOfflineRender()
delete m_depthMap; delete m_depthMap;
} }
void ModelOfflineRender::updateMesh(MeshLoader *mesh) void ModelOffscreenRender::updateMesh(Model *mesh)
{ {
delete m_mesh; delete m_mesh;
m_mesh = mesh; m_mesh = mesh;
} }
void ModelOfflineRender::setRenderThread(QThread *thread) void ModelOffscreenRender::setRenderThread(QThread *thread)
{ {
//this->moveToThread(thread); this->moveToThread(thread);
} }
void ModelOfflineRender::setXRotation(int angle) void ModelOffscreenRender::setXRotation(int angle)
{ {
m_xRot = angle; m_xRot = angle;
} }
void ModelOfflineRender::setYRotation(int angle) void ModelOffscreenRender::setYRotation(int angle)
{ {
m_yRot = angle; m_yRot = angle;
} }
void ModelOfflineRender::setZRotation(int angle) void ModelOffscreenRender::setZRotation(int angle)
{ {
m_zRot = angle; m_zRot = angle;
} }
void ModelOfflineRender::setRenderPurpose(int purpose) void ModelOffscreenRender::setRenderPurpose(int purpose)
{ {
m_renderPurpose = purpose; m_renderPurpose = purpose;
} }
void ModelOfflineRender::setToonShading(bool toonShading) void ModelOffscreenRender::setToonShading(bool toonShading)
{ {
m_toonShading = toonShading; m_toonShading = toonShading;
} }
void ModelOfflineRender::updateToonNormalAndDepthMaps(QImage *normalMap, QImage *depthMap) void ModelOffscreenRender::updateToonNormalAndDepthMaps(QImage *normalMap, QImage *depthMap)
{ {
delete m_normalMap; delete m_normalMap;
m_normalMap = normalMap; m_normalMap = normalMap;
@ -67,7 +67,7 @@ void ModelOfflineRender::updateToonNormalAndDepthMaps(QImage *normalMap, QImage
m_depthMap = depthMap; m_depthMap = depthMap;
} }
QImage ModelOfflineRender::toImage(const QSize &size) QImage ModelOffscreenRender::toImage(const QSize &size)
{ {
QImage image; QImage image;
@ -110,7 +110,7 @@ QImage ModelOfflineRender::toImage(const QSize &size)
0 == strstr(versionString, "Mesa")) { 0 == strstr(versionString, "Mesa")) {
isCoreProfile = m_context->format().profile() == QSurfaceFormat::CoreProfile; isCoreProfile = m_context->format().profile() == QSurfaceFormat::CoreProfile;
} }
ModelShaderProgram *program = new ModelShaderProgram(isCoreProfile); ModelShaderProgram *program = new ModelShaderProgram(isCoreProfile);
ModelMeshBinder meshBinder; ModelMeshBinder meshBinder;
meshBinder.initialize(); meshBinder.initialize();
@ -170,14 +170,16 @@ QImage ModelOfflineRender::toImage(const QSize &size)
m_mesh = nullptr; m_mesh = nullptr;
} }
m_context->functions()->glFlush(); m_context->functions()->glFlush();
image = renderFbo->toImage(); image = renderFbo->toImage();
qDebug() << "bindDefault begin...";
renderFbo->bindDefault(); renderFbo->bindDefault();
qDebug() << "bindDefault end";
delete renderFbo; delete renderFbo;
m_context->doneCurrent(); m_context->doneCurrent();
delete m_context; delete m_context;
m_context = nullptr; m_context = nullptr;

View File

@ -1,5 +1,5 @@
#ifndef DUST3D_MODEL_OFFLINE_RENDER_H #ifndef DUST3D_MODEL_OFFSCREEN_RENDER_H
#define DUST3D_MODEL_OFFLINE_RENDER_H #define DUST3D_MODEL_OFFSCREEN_RENDER_H
#include <QOffscreenSurface> #include <QOffscreenSurface>
#include <QScreen> #include <QScreen>
#include <QOpenGLFunctions> #include <QOpenGLFunctions>
@ -8,19 +8,19 @@
#include <QThread> #include <QThread>
#include "modelshaderprogram.h" #include "modelshaderprogram.h"
#include "modelmeshbinder.h" #include "modelmeshbinder.h"
#include "meshloader.h" #include "model.h"
class ModelOfflineRender : QOffscreenSurface class ModelOffscreenRender : QOffscreenSurface
{ {
public: public:
ModelOfflineRender(const QSurfaceFormat &format, QScreen *targetScreen = Q_NULLPTR); ModelOffscreenRender(const QSurfaceFormat &format, QScreen *targetScreen = Q_NULLPTR);
~ModelOfflineRender(); ~ModelOffscreenRender();
void setXRotation(int angle); void setXRotation(int angle);
void setYRotation(int angle); void setYRotation(int angle);
void setZRotation(int angle); void setZRotation(int angle);
void setRenderPurpose(int purpose); void setRenderPurpose(int purpose);
void setRenderThread(QThread *thread); void setRenderThread(QThread *thread);
void updateMesh(MeshLoader *mesh); void updateMesh(Model *mesh);
void updateToonNormalAndDepthMaps(QImage *normalMap, QImage *depthMap); void updateToonNormalAndDepthMaps(QImage *normalMap, QImage *depthMap);
void setToonShading(bool toonShading); void setToonShading(bool toonShading);
QImage toImage(const QSize &size); QImage toImage(const QSize &size);
@ -30,7 +30,7 @@ private:
int m_zRot = 0; int m_zRot = 0;
int m_renderPurpose = 0; int m_renderPurpose = 0;
QOpenGLContext *m_context = nullptr; QOpenGLContext *m_context = nullptr;
MeshLoader *m_mesh = nullptr; Model *m_mesh = nullptr;
QImage *m_normalMap = nullptr; QImage *m_normalMap = nullptr;
QImage *m_depthMap = nullptr; QImage *m_depthMap = nullptr;
bool m_toonShading = false; bool m_toonShading = false;

View File

@ -114,7 +114,7 @@ void ModelWidget::setZRotation(int angle)
} }
} }
MeshLoader *ModelWidget::fetchCurrentMesh() Model *ModelWidget::fetchCurrentMesh()
{ {
return m_meshBinder.fetchCurrentMesh(); return m_meshBinder.fetchCurrentMesh();
} }
@ -422,7 +422,7 @@ void ModelWidget::setMousePickRadius(float radius)
update(); update();
} }
void ModelWidget::updateMesh(MeshLoader *mesh) void ModelWidget::updateMesh(Model *mesh)
{ {
m_meshBinder.updateMesh(mesh); m_meshBinder.updateMesh(mesh);
emit renderParametersChanged(); emit renderParametersChanged();

View File

@ -9,7 +9,7 @@
#include <QRubberBand> #include <QRubberBand>
#include <QVector2D> #include <QVector2D>
#include <QTimer> #include <QTimer>
#include "meshloader.h" #include "model.h"
#include "modelshaderprogram.h" #include "modelshaderprogram.h"
#include "modelmeshbinder.h" #include "modelmeshbinder.h"
@ -35,8 +35,8 @@ public:
{ {
m_transparent = t; m_transparent = t;
} }
MeshLoader *fetchCurrentMesh(); Model *fetchCurrentMesh();
void updateMesh(MeshLoader *mesh); void updateMesh(Model *mesh);
void setGraphicsFunctions(SkeletonGraphicsFunctions *graphicsFunctions); void setGraphicsFunctions(SkeletonGraphicsFunctions *graphicsFunctions);
void toggleWireframe(); void toggleWireframe();
void toggleRotation(); void toggleRotation();

View File

@ -80,7 +80,7 @@ std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QSt
return &frames; return &frames;
} }
void MotionsGenerator::generatePreviewsForOutcomes(const std::vector<std::pair<float, JointNodeTree>> &outcomes, std::vector<std::pair<float, MeshLoader *>> &previews) void MotionsGenerator::generatePreviewsForOutcomes(const std::vector<std::pair<float, JointNodeTree>> &outcomes, std::vector<std::pair<float, Model *>> &previews)
{ {
for (const auto &item: outcomes) { for (const auto &item: outcomes) {
PoseMeshCreator *poseMeshCreator = new PoseMeshCreator(item.second.nodes(), m_outcome, m_rigWeights); PoseMeshCreator *poseMeshCreator = new PoseMeshCreator(item.second.nodes(), m_outcome, m_rigWeights);
@ -114,7 +114,7 @@ const std::vector<std::pair<float, JointNodeTree>> &MotionsGenerator::getProcedu
std::vector<std::pair<float, JointNodeTree>> &resultFrames = m_proceduralAnimations[(int)proceduralAnimation]; std::vector<std::pair<float, JointNodeTree>> &resultFrames = m_proceduralAnimations[(int)proceduralAnimation];
if (ProceduralAnimation::FallToDeath == proceduralAnimation) { if (ProceduralAnimation::FallToDeath == proceduralAnimation) {
#if ENABLE_PROCEDURAL_DEBUG #if ENABLE_PROCEDURAL_DEBUG
std::vector<MeshLoader *> &resultPreviews = m_proceduralDebugPreviews[(int)proceduralAnimation]; std::vector<Model *> &resultPreviews = m_proceduralDebugPreviews[(int)proceduralAnimation];
#endif #endif
RagDoll ragdoll(&m_rigBones, initialJointNodeTree); RagDoll ragdoll(&m_rigBones, initialJointNodeTree);
float stepSeconds = 1.0 / 60; float stepSeconds = 1.0 / 60;
@ -124,7 +124,7 @@ const std::vector<std::pair<float, JointNodeTree>> &MotionsGenerator::getProcedu
while (steps < maxSteps && ragdoll.stepSimulation(stepSeconds)) { while (steps < maxSteps && ragdoll.stepSimulation(stepSeconds)) {
resultFrames.push_back(std::make_pair(stepSeconds * 2, ragdoll.getStepJointNodeTree())); resultFrames.push_back(std::make_pair(stepSeconds * 2, ragdoll.getStepJointNodeTree()));
#if ENABLE_PROCEDURAL_DEBUG #if ENABLE_PROCEDURAL_DEBUG
MeshLoader *preview = buildBoundingBoxMesh(ragdoll.getStepBonePositions()); Model *preview = buildBoundingBoxMesh(ragdoll.getStepBonePositions());
resultPreviews.push_back(preview); resultPreviews.push_back(preview);
#endif #endif
++steps; ++steps;
@ -178,7 +178,7 @@ float MotionsGenerator::calculateMotionDuration(const QUuid &motionId, std::set<
return totalDuration; return totalDuration;
} }
void MotionsGenerator::generateMotion(const QUuid &motionId, std::set<QUuid> &visited, std::vector<std::pair<float, JointNodeTree>> &outcomes, std::vector<MeshLoader *> *previews) void MotionsGenerator::generateMotion(const QUuid &motionId, std::set<QUuid> &visited, std::vector<std::pair<float, JointNodeTree>> &outcomes, std::vector<Model *> *previews)
{ {
if (visited.find(motionId) != visited.end()) { if (visited.find(motionId) != visited.end()) {
qDebug() << "Found recursive motion link"; qDebug() << "Found recursive motion link";
@ -386,7 +386,7 @@ const JointNodeTree *MotionsGenerator::findClipEndJointNodeTree(const MotionClip
return nullptr; return nullptr;
} }
std::vector<std::pair<float, MeshLoader *>> MotionsGenerator::takeResultPreviewMeshs(const QUuid &motionId) std::vector<std::pair<float, Model *>> MotionsGenerator::takeResultPreviewMeshs(const QUuid &motionId)
{ {
auto findResult = m_resultPreviewMeshs.find(motionId); auto findResult = m_resultPreviewMeshs.find(motionId);
if (findResult == m_resultPreviewMeshs.end()) if (findResult == m_resultPreviewMeshs.end())
@ -413,7 +413,7 @@ void MotionsGenerator::generate()
for (const auto &motionId: m_requiredMotionIds) { for (const auto &motionId: m_requiredMotionIds) {
std::set<QUuid> visited; std::set<QUuid> visited;
#if ENABLE_PROCEDURAL_DEBUG #if ENABLE_PROCEDURAL_DEBUG
std::vector<MeshLoader *> previews; std::vector<Model *> previews;
generateMotion(motionId, visited, m_resultJointNodeTrees[motionId], &previews); generateMotion(motionId, visited, m_resultJointNodeTrees[motionId], &previews);
#else #else
generateMotion(motionId, visited, m_resultJointNodeTrees[motionId]); generateMotion(motionId, visited, m_resultJointNodeTrees[motionId]);

View File

@ -4,7 +4,7 @@
#include <vector> #include <vector>
#include <map> #include <map>
#include <set> #include <set>
#include "meshloader.h" #include "model.h"
#include "rigger.h" #include "rigger.h"
#include "jointnodetree.h" #include "jointnodetree.h"
#include "document.h" #include "document.h"
@ -24,7 +24,7 @@ public:
void addPoseToLibrary(const QUuid &poseId, const std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>> &frames, float yTranslationScale); void addPoseToLibrary(const QUuid &poseId, const std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>> &frames, float yTranslationScale);
void addMotionToLibrary(const QUuid &motionId, const std::vector<MotionClip> &clips); void addMotionToLibrary(const QUuid &motionId, const std::vector<MotionClip> &clips);
void addRequirement(const QUuid &motionId); void addRequirement(const QUuid &motionId);
std::vector<std::pair<float, MeshLoader *>> takeResultPreviewMeshs(const QUuid &motionId); std::vector<std::pair<float, Model *>> takeResultPreviewMeshs(const QUuid &motionId);
std::vector<std::pair<float, JointNodeTree>> takeResultJointNodeTrees(const QUuid &motionId); std::vector<std::pair<float, JointNodeTree>> takeResultJointNodeTrees(const QUuid &motionId);
const std::set<QUuid> &requiredMotionIds(); const std::set<QUuid> &requiredMotionIds();
const std::set<QUuid> &generatedMotionIds(); const std::set<QUuid> &generatedMotionIds();
@ -37,14 +37,14 @@ public slots:
private: private:
void generateMotion(const QUuid &motionId, std::set<QUuid> &visited, std::vector<std::pair<float, JointNodeTree>> &outcomes, void generateMotion(const QUuid &motionId, std::set<QUuid> &visited, std::vector<std::pair<float, JointNodeTree>> &outcomes,
std::vector<MeshLoader *> *previews=nullptr); std::vector<Model *> *previews=nullptr);
const JointNodeTree &poseJointNodeTree(const QUuid &poseId, int frame); const JointNodeTree &poseJointNodeTree(const QUuid &poseId, int frame);
JointNodeTree generateInterpolation(InterpolationType interpolationType, const JointNodeTree &first, const JointNodeTree &second, float progress); JointNodeTree generateInterpolation(InterpolationType interpolationType, const JointNodeTree &first, const JointNodeTree &second, float progress);
const JointNodeTree *findClipBeginJointNodeTree(const MotionClip &clip); const JointNodeTree *findClipBeginJointNodeTree(const MotionClip &clip);
const JointNodeTree *findClipEndJointNodeTree(const MotionClip &clip); const JointNodeTree *findClipEndJointNodeTree(const MotionClip &clip);
std::vector<MotionClip> *findMotionClips(const QUuid &motionId); std::vector<MotionClip> *findMotionClips(const QUuid &motionId);
std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>> *findPoseFrames(const QUuid &poseId); std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>> *findPoseFrames(const QUuid &poseId);
void generatePreviewsForOutcomes(const std::vector<std::pair<float, JointNodeTree>> &outcomes, std::vector<std::pair<float, MeshLoader *>> &previews); void generatePreviewsForOutcomes(const std::vector<std::pair<float, JointNodeTree>> &outcomes, std::vector<std::pair<float, Model *>> &previews);
float calculateMotionDuration(const QUuid &motionId, std::set<QUuid> &visited); float calculateMotionDuration(const QUuid &motionId, std::set<QUuid> &visited);
float calculatePoseDuration(const QUuid &poseId); float calculatePoseDuration(const QUuid &poseId);
float calculateProceduralAnimationDuration(ProceduralAnimation proceduralAnimation, float calculateProceduralAnimationDuration(ProceduralAnimation proceduralAnimation,
@ -57,7 +57,7 @@ private:
std::map<int, RiggerVertexWeights> m_rigWeights; std::map<int, RiggerVertexWeights> m_rigWeights;
std::map<int, std::vector<std::pair<float, JointNodeTree>>> m_proceduralAnimations; std::map<int, std::vector<std::pair<float, JointNodeTree>>> m_proceduralAnimations;
#if ENABLE_PROCEDURAL_DEBUG #if ENABLE_PROCEDURAL_DEBUG
std::map<int, std::vector<MeshLoader *>> m_proceduralDebugPreviews; std::map<int, std::vector<Model *>> m_proceduralDebugPreviews;
#endif #endif
Outcome m_outcome; Outcome m_outcome;
std::map<QUuid, std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>>> m_poses; std::map<QUuid, std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>>> m_poses;
@ -65,7 +65,7 @@ private:
std::map<QUuid, std::vector<MotionClip>> m_motions; std::map<QUuid, std::vector<MotionClip>> m_motions;
std::set<QUuid> m_requiredMotionIds; std::set<QUuid> m_requiredMotionIds;
std::set<QUuid> m_generatedMotionIds; std::set<QUuid> m_generatedMotionIds;
std::map<QUuid, std::vector<std::pair<float, MeshLoader *>>> m_resultPreviewMeshs; std::map<QUuid, std::vector<std::pair<float, Model *>>> m_resultPreviewMeshs;
std::map<QUuid, std::vector<std::pair<float, JointNodeTree>>> m_resultJointNodeTrees; std::map<QUuid, std::vector<std::pair<float, JointNodeTree>>> m_resultJointNodeTrees;
std::map<std::pair<QUuid, int>, JointNodeTree> m_poseJointNodeTreeMap; std::map<std::pair<QUuid, int>, JointNodeTree> m_poseJointNodeTreeMap;
Poser *m_poser = nullptr; Poser *m_poser = nullptr;

View File

@ -82,7 +82,7 @@ void MotionWidget::updatePreview()
qDebug() << "Motion not found:" << m_motionId; qDebug() << "Motion not found:" << m_motionId;
return; return;
} }
MeshLoader *previewMesh = motion->takePreviewMesh(); Model *previewMesh = motion->takePreviewMesh();
m_previewWidget->updateMesh(previewMesh); m_previewWidget->updateMesh(previewMesh);
} }

View File

@ -3,6 +3,7 @@
#include <QRadialGradient> #include <QRadialGradient>
#include <QBrush> #include <QBrush>
#include <QPainter> #include <QPainter>
#include <QGuiApplication>
#include "mousepicker.h" #include "mousepicker.h"
#include "util.h" #include "util.h"
#include "imageforever.h" #include "imageforever.h"
@ -114,7 +115,7 @@ void MousePicker::pick()
void MousePicker::process() void MousePicker::process()
{ {
pick(); pick();
this->moveToThread(QGuiApplication::instance()->thread());
emit finished(); emit finished();
} }

View File

@ -1,3 +1,5 @@
#include <QDebug>
#include <QGuiApplication>
#include "normalanddepthmapsgenerator.h" #include "normalanddepthmapsgenerator.h"
NormalAndDepthMapsGenerator::NormalAndDepthMapsGenerator(ModelWidget *modelWidget) NormalAndDepthMapsGenerator::NormalAndDepthMapsGenerator(ModelWidget *modelWidget)
@ -8,26 +10,26 @@ NormalAndDepthMapsGenerator::NormalAndDepthMapsGenerator(ModelWidget *modelWidge
m_depthMapRender = createOfflineRender(modelWidget, 2); m_depthMapRender = createOfflineRender(modelWidget, 2);
} }
void NormalAndDepthMapsGenerator::updateMesh(MeshLoader *mesh) void NormalAndDepthMapsGenerator::updateMesh(Model *mesh)
{ {
if (nullptr == mesh) { if (nullptr == mesh) {
m_normalMapRender->updateMesh(nullptr); m_normalMapRender->updateMesh(nullptr);
m_depthMapRender->updateMesh(nullptr); m_depthMapRender->updateMesh(nullptr);
return; return;
} }
m_normalMapRender->updateMesh(new MeshLoader(*mesh)); m_normalMapRender->updateMesh(new Model(*mesh));
m_depthMapRender->updateMesh(mesh); m_depthMapRender->updateMesh(mesh);
} }
void NormalAndDepthMapsGenerator::setRenderThread(QThread *thread) void NormalAndDepthMapsGenerator::setRenderThread(QThread *thread)
{ {
//m_normalMapRender->setRenderThread(thread); m_normalMapRender->setRenderThread(thread);
//m_depthMapRender->setRenderThread(thread); m_depthMapRender->setRenderThread(thread);
} }
ModelOfflineRender *NormalAndDepthMapsGenerator::createOfflineRender(ModelWidget *modelWidget, int purpose) ModelOffscreenRender *NormalAndDepthMapsGenerator::createOfflineRender(ModelWidget *modelWidget, int purpose)
{ {
ModelOfflineRender *offlineRender = new ModelOfflineRender(modelWidget->format()); ModelOffscreenRender *offlineRender = new ModelOffscreenRender(modelWidget->format());
offlineRender->setXRotation(modelWidget->xRot()); offlineRender->setXRotation(modelWidget->xRot());
offlineRender->setYRotation(modelWidget->yRot()); offlineRender->setYRotation(modelWidget->yRot());
offlineRender->setZRotation(modelWidget->zRot()); offlineRender->setZRotation(modelWidget->zRot());
@ -52,6 +54,9 @@ void NormalAndDepthMapsGenerator::generate()
void NormalAndDepthMapsGenerator::process() void NormalAndDepthMapsGenerator::process()
{ {
generate(); generate();
m_normalMapRender->setRenderThread(QGuiApplication::instance()->thread());
m_depthMapRender->setRenderThread(QGuiApplication::instance()->thread());
this->moveToThread(QGuiApplication::instance()->thread());
emit finished(); emit finished();
} }

View File

@ -3,15 +3,15 @@
#include <QObject> #include <QObject>
#include <QImage> #include <QImage>
#include "modelwidget.h" #include "modelwidget.h"
#include "modelofflinerender.h" #include "modeloffscreenrender.h"
#include "meshloader.h" #include "model.h"
class NormalAndDepthMapsGenerator : public QObject class NormalAndDepthMapsGenerator : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
NormalAndDepthMapsGenerator(ModelWidget *modelWidget); NormalAndDepthMapsGenerator(ModelWidget *modelWidget);
void updateMesh(MeshLoader *mesh); void updateMesh(Model *mesh);
void setRenderThread(QThread *thread); void setRenderThread(QThread *thread);
~NormalAndDepthMapsGenerator(); ~NormalAndDepthMapsGenerator();
QImage *takeNormalMap(); QImage *takeNormalMap();
@ -21,13 +21,13 @@ signals:
public slots: public slots:
void process(); void process();
private: private:
ModelOfflineRender *m_normalMapRender = nullptr; ModelOffscreenRender *m_normalMapRender = nullptr;
ModelOfflineRender *m_depthMapRender = nullptr; ModelOffscreenRender *m_depthMapRender = nullptr;
QSize m_viewPortSize; QSize m_viewPortSize;
QImage *m_normalMap = nullptr; QImage *m_normalMap = nullptr;
QImage *m_depthMap = nullptr; QImage *m_depthMap = nullptr;
ModelOfflineRender *createOfflineRender(ModelWidget *modelWidget, int purpose); ModelOffscreenRender *createOfflineRender(ModelWidget *modelWidget, int purpose);
void generate(); void generate();
}; };

View File

@ -792,7 +792,7 @@ void PartWidget::updatePreview()
return; return;
} }
//m_previewLabel->setPixmap(QPixmap::fromImage(part->preview)); //m_previewLabel->setPixmap(QPixmap::fromImage(part->preview));
MeshLoader *previewMesh = part->takePreviewMesh(); Model *previewMesh = part->takePreviewMesh();
m_previewWidget->updateMesh(previewMesh); m_previewWidget->updateMesh(previewMesh);
if (PartTarget::CutFace == part->target) { if (PartTarget::CutFace == part->target) {
if (0 != m_previewWidget->xRot()) { if (0 != m_previewWidget->xRot()) {

View File

@ -16,9 +16,9 @@ PoseMeshCreator::~PoseMeshCreator()
delete m_resultMesh; delete m_resultMesh;
} }
MeshLoader *PoseMeshCreator::takeResultMesh() Model *PoseMeshCreator::takeResultMesh()
{ {
MeshLoader *resultMesh = m_resultMesh; Model *resultMesh = m_resultMesh;
m_resultMesh = nullptr; m_resultMesh = nullptr;
return resultMesh; return resultMesh;
} }

View File

@ -1,7 +1,7 @@
#ifndef DUST3D_POSE_MESH_CREATOR_H #ifndef DUST3D_POSE_MESH_CREATOR_H
#define DUST3D_POSE_MESH_CREATOR_H #define DUST3D_POSE_MESH_CREATOR_H
#include <QObject> #include <QObject>
#include "meshloader.h" #include "model.h"
#include "jointnodetree.h" #include "jointnodetree.h"
#include "outcome.h" #include "outcome.h"
@ -16,14 +16,14 @@ public:
const std::map<int, RiggerVertexWeights> &resultWeights); const std::map<int, RiggerVertexWeights> &resultWeights);
~PoseMeshCreator(); ~PoseMeshCreator();
void createMesh(); void createMesh();
MeshLoader *takeResultMesh(); Model *takeResultMesh();
public slots: public slots:
void process(); void process();
private: private:
std::vector<JointNode> m_resultNodes; std::vector<JointNode> m_resultNodes;
Outcome m_outcome; Outcome m_outcome;
std::map<int, RiggerVertexWeights> m_resultWeights; std::map<int, RiggerVertexWeights> m_resultWeights;
MeshLoader *m_resultMesh = nullptr; Model *m_resultMesh = nullptr;
}; };
#endif #endif

View File

@ -37,11 +37,11 @@ bool PosePreviewManager::postUpdate(const Poser &poser,
return true; return true;
} }
MeshLoader *PosePreviewManager::takeResultPreviewMesh() Model *PosePreviewManager::takeResultPreviewMesh()
{ {
if (nullptr == m_previewMesh) if (nullptr == m_previewMesh)
return nullptr; return nullptr;
return new MeshLoader(*m_previewMesh); return new Model(*m_previewMesh);
} }
void PosePreviewManager::poseMeshReady() void PosePreviewManager::poseMeshReady()

View File

@ -4,7 +4,7 @@
#include "document.h" #include "document.h"
#include "poser.h" #include "poser.h"
#include "posemeshcreator.h" #include "posemeshcreator.h"
#include "meshloader.h" #include "model.h"
class PosePreviewManager : public QObject class PosePreviewManager : public QObject
{ {
@ -16,7 +16,7 @@ public:
bool postUpdate(const Poser &poser, bool postUpdate(const Poser &poser,
const Outcome &outcome, const Outcome &outcome,
const std::map<int, RiggerVertexWeights> &resultWeights); const std::map<int, RiggerVertexWeights> &resultWeights);
MeshLoader *takeResultPreviewMesh(); Model *takeResultPreviewMesh();
private slots: private slots:
void poseMeshReady(); void poseMeshReady();
signals: signals:
@ -24,7 +24,7 @@ signals:
void renderDone(); void renderDone();
private: private:
PoseMeshCreator *m_poseMeshCreator = nullptr; PoseMeshCreator *m_poseMeshCreator = nullptr;
MeshLoader *m_previewMesh = nullptr; Model *m_previewMesh = nullptr;
}; };
#endif #endif

View File

@ -34,9 +34,9 @@ const std::set<std::pair<QUuid, int>> &PosePreviewsGenerator::generatedPreviewPo
return m_generatedPoseIdAndFrames; return m_generatedPoseIdAndFrames;
} }
MeshLoader *PosePreviewsGenerator::takePreview(std::pair<QUuid, int> idAndFrame) Model *PosePreviewsGenerator::takePreview(std::pair<QUuid, int> idAndFrame)
{ {
MeshLoader *resultMesh = m_previews[idAndFrame]; Model *resultMesh = m_previews[idAndFrame];
m_previews[idAndFrame] = nullptr; m_previews[idAndFrame] = nullptr;
return resultMesh; return resultMesh;
} }

View File

@ -4,7 +4,7 @@
#include <map> #include <map>
#include <QUuid> #include <QUuid>
#include <vector> #include <vector>
#include "meshloader.h" #include "model.h"
#include "rigger.h" #include "rigger.h"
#include "outcome.h" #include "outcome.h"
#include "rigtype.h" #include "rigtype.h"
@ -20,7 +20,7 @@ public:
~PosePreviewsGenerator(); ~PosePreviewsGenerator();
void addPose(std::pair<QUuid, int> idAndFrame, const std::map<QString, std::map<QString, QString>> &pose); void addPose(std::pair<QUuid, int> idAndFrame, const std::map<QString, std::map<QString, QString>> &pose);
const std::set<std::pair<QUuid, int>> &generatedPreviewPoseIdAndFrames(); const std::set<std::pair<QUuid, int>> &generatedPreviewPoseIdAndFrames();
MeshLoader *takePreview(std::pair<QUuid, int> idAndFrame); Model *takePreview(std::pair<QUuid, int> idAndFrame);
signals: signals:
void finished(); void finished();
public slots: public slots:
@ -31,7 +31,7 @@ private:
std::map<int, RiggerVertexWeights> m_rigWeights; std::map<int, RiggerVertexWeights> m_rigWeights;
Outcome *m_outcome = nullptr; Outcome *m_outcome = nullptr;
std::vector<std::pair<std::pair<QUuid, int>, std::map<QString, std::map<QString, QString>>>> m_poses; std::vector<std::pair<std::pair<QUuid, int>, std::map<QString, std::map<QString, QString>>>> m_poses;
std::map<std::pair<QUuid, int>, MeshLoader *> m_previews; std::map<std::pair<QUuid, int>, Model *> m_previews;
std::set<std::pair<QUuid, int>> m_generatedPoseIdAndFrames; std::set<std::pair<QUuid, int>> m_generatedPoseIdAndFrames;
}; };

View File

@ -82,7 +82,7 @@ void PoseWidget::updatePreview()
qDebug() << "Pose not found:" << m_poseId; qDebug() << "Pose not found:" << m_poseId;
return; return;
} }
MeshLoader *previewMesh = pose->takePreviewMesh(); Model *previewMesh = pose->takePreviewMesh();
m_previewWidget->updateMesh(previewMesh); m_previewWidget->updateMesh(previewMesh);
} }

View File

@ -94,16 +94,16 @@ std::map<int, RiggerVertexWeights> *RigGenerator::takeResultWeights()
return resultWeights; return resultWeights;
} }
MeshLoader *RigGenerator::takeResultMesh() Model *RigGenerator::takeResultMesh()
{ {
MeshLoader *resultMesh = m_resultMesh; Model *resultMesh = m_resultMesh;
m_resultMesh = nullptr; m_resultMesh = nullptr;
return resultMesh; return resultMesh;
} }
bool RigGenerator::isSucceed() bool RigGenerator::isSuccessful()
{ {
return m_isSucceed; return m_isSuccessful;
} }
const std::vector<std::pair<QtMsgType, QString>> &RigGenerator::messages() const std::vector<std::pair<QtMsgType, QString>> &RigGenerator::messages()
@ -603,7 +603,7 @@ void RigGenerator::buildSkeleton()
} }
} }
m_isSucceed = true; m_isSuccessful = true;
//for (size_t i = 0; i < m_resultBones->size(); ++i) { //for (size_t i = 0; i < m_resultBones->size(); ++i) {
// const auto &bone = (*m_resultBones)[i]; // const auto &bone = (*m_resultBones)[i];
@ -617,7 +617,7 @@ void RigGenerator::buildSkeleton()
void RigGenerator::computeSkinWeights() void RigGenerator::computeSkinWeights()
{ {
if (!m_isSucceed) if (!m_isSuccessful)
return; return;
auto collectNodeIndices = [&](size_t chainIndex, auto collectNodeIndices = [&](size_t chainIndex,
@ -1026,7 +1026,7 @@ void RigGenerator::buildDemoMesh()
// Blend vertices colors according to bone weights // Blend vertices colors according to bone weights
std::vector<QColor> inputVerticesColors(m_outcome->vertices.size(), Qt::black); std::vector<QColor> inputVerticesColors(m_outcome->vertices.size(), Qt::black);
if (m_isSucceed) { if (m_isSuccessful) {
const auto &resultWeights = *m_resultWeights; const auto &resultWeights = *m_resultWeights;
const auto &resultBones = *m_resultBones; const auto &resultBones = *m_resultBones;
@ -1060,7 +1060,7 @@ void RigGenerator::buildDemoMesh()
ShaderVertex *triangleVertices = nullptr; ShaderVertex *triangleVertices = nullptr;
int triangleVerticesNum = 0; int triangleVerticesNum = 0;
if (m_isSucceed) { if (m_isSuccessful) {
triangleVertices = new ShaderVertex[m_outcome->triangles.size() * 3]; triangleVertices = new ShaderVertex[m_outcome->triangles.size() * 3];
const QVector3D defaultUv = QVector3D(0, 0, 0); const QVector3D defaultUv = QVector3D(0, 0, 0);
const QVector3D defaultTangents = QVector3D(0, 0, 0); const QVector3D defaultTangents = QVector3D(0, 0, 0);
@ -1087,8 +1087,8 @@ void RigGenerator::buildDemoMesh()
currentVertex.normX = sourceNormal->x(); currentVertex.normX = sourceNormal->x();
currentVertex.normY = sourceNormal->y(); currentVertex.normY = sourceNormal->y();
currentVertex.normZ = sourceNormal->z(); currentVertex.normZ = sourceNormal->z();
currentVertex.metalness = MeshLoader::m_defaultMetalness; currentVertex.metalness = Model::m_defaultMetalness;
currentVertex.roughness = MeshLoader::m_defaultRoughness; currentVertex.roughness = Model::m_defaultRoughness;
currentVertex.tangentX = sourceTangent->x(); currentVertex.tangentX = sourceTangent->x();
currentVertex.tangentY = sourceTangent->y(); currentVertex.tangentY = sourceTangent->y();
currentVertex.tangentZ = sourceTangent->z(); currentVertex.tangentZ = sourceTangent->z();
@ -1101,7 +1101,7 @@ void RigGenerator::buildDemoMesh()
ShaderVertex *edgeVertices = nullptr; ShaderVertex *edgeVertices = nullptr;
int edgeVerticesNum = 0; int edgeVerticesNum = 0;
if (m_isSucceed) { if (m_isSuccessful) {
const auto &resultBones = *m_resultBones; const auto &resultBones = *m_resultBones;
std::vector<std::tuple<QVector3D, QVector3D, float, float, QColor>> boxes; std::vector<std::tuple<QVector3D, QVector3D, float, float, QColor>> boxes;
for (const auto &bone: resultBones) { for (const auto &bone: resultBones) {
@ -1122,7 +1122,7 @@ void RigGenerator::buildDemoMesh()
m_debugEdgeVerticesNum = 0; m_debugEdgeVerticesNum = 0;
} }
m_resultMesh = new MeshLoader(triangleVertices, triangleVerticesNum, m_resultMesh = new Model(triangleVertices, triangleVerticesNum,
edgeVertices, edgeVerticesNum); edgeVertices, edgeVerticesNum);
} }

View File

@ -5,7 +5,7 @@
#include <QDebug> #include <QDebug>
#include <unordered_set> #include <unordered_set>
#include "outcome.h" #include "outcome.h"
#include "meshloader.h" #include "model.h"
#include "rigger.h" #include "rigger.h"
#include "rigtype.h" #include "rigtype.h"
@ -15,12 +15,12 @@ class RigGenerator : public QObject
public: public:
RigGenerator(RigType rigType, const Outcome &outcome); RigGenerator(RigType rigType, const Outcome &outcome);
~RigGenerator(); ~RigGenerator();
MeshLoader *takeResultMesh(); Model *takeResultMesh();
std::vector<RiggerBone> *takeResultBones(); std::vector<RiggerBone> *takeResultBones();
std::map<int, RiggerVertexWeights> *takeResultWeights(); std::map<int, RiggerVertexWeights> *takeResultWeights();
const std::vector<std::pair<QtMsgType, QString>> &messages(); const std::vector<std::pair<QtMsgType, QString>> &messages();
Outcome *takeOutcome(); Outcome *takeOutcome();
bool isSucceed(); bool isSuccessful();
void generate(); void generate();
signals: signals:
void finished(); void finished();
@ -37,7 +37,7 @@ private:
RigType m_rigType = RigType::None; RigType m_rigType = RigType::None;
Outcome *m_outcome = nullptr; Outcome *m_outcome = nullptr;
MeshLoader *m_resultMesh = nullptr; Model *m_resultMesh = nullptr;
std::vector<RiggerBone> *m_resultBones = nullptr; std::vector<RiggerBone> *m_resultBones = nullptr;
std::map<int, RiggerVertexWeights> *m_resultWeights = nullptr; std::map<int, RiggerVertexWeights> *m_resultWeights = nullptr;
std::vector<std::pair<QtMsgType, QString>> m_messages; std::vector<std::pair<QtMsgType, QString>> m_messages;
@ -61,7 +61,7 @@ private:
ShaderVertex *m_debugEdgeVertices = nullptr; ShaderVertex *m_debugEdgeVertices = nullptr;
int m_debugEdgeVerticesNum = 0; int m_debugEdgeVerticesNum = 0;
bool m_isSpineVertical = false; bool m_isSpineVertical = false;
bool m_isSucceed = false; bool m_isSuccessful = false;
void buildNeighborMap(); void buildNeighborMap();
void buildBoneNodeChain(); void buildBoneNodeChain();
void buildSkeleton(); void buildSkeleton();

View File

@ -1,3 +1,4 @@
#ifndef DUST3D_SKELETON_DOCUMENT_H #ifndef DUST3D_SKELETON_DOCUMENT_H
#define DUST3D_SKELETON_DOCUMENT_H #define DUST3D_SKELETON_DOCUMENT_H
#include <QUuid> #include <QUuid>
@ -8,7 +9,7 @@
#include <QByteArray> #include <QByteArray>
#include "bonemark.h" #include "bonemark.h"
#include "theme.h" #include "theme.h"
#include "meshloader.h" #include "model.h"
#include "cutface.h" #include "cutface.h"
#include "parttarget.h" #include "parttarget.h"
#include "partbase.h" #include "partbase.h"
@ -181,6 +182,7 @@ public:
float hollowThickness; float hollowThickness;
bool countershaded; bool countershaded;
bool gridded; bool gridded;
QUuid fillMeshLinkedId;
SkeletonPart(const QUuid &withId=QUuid()) : SkeletonPart(const QUuid &withId=QUuid()) :
visible(true), visible(true),
locked(false), locked(false),
@ -318,20 +320,20 @@ public:
colorSolubility = other.colorSolubility; colorSolubility = other.colorSolubility;
countershaded = other.countershaded; countershaded = other.countershaded;
} }
void updatePreviewMesh(MeshLoader *previewMesh) void updatePreviewMesh(Model *previewMesh)
{ {
delete m_previewMesh; delete m_previewMesh;
m_previewMesh = previewMesh; m_previewMesh = previewMesh;
} }
MeshLoader *takePreviewMesh() const Model *takePreviewMesh() const
{ {
if (nullptr == m_previewMesh) if (nullptr == m_previewMesh)
return nullptr; return nullptr;
return new MeshLoader(*m_previewMesh); return new Model(*m_previewMesh);
} }
private: private:
Q_DISABLE_COPY(SkeletonPart); Q_DISABLE_COPY(SkeletonPart);
MeshLoader *m_previewMesh = nullptr; Model *m_previewMesh = nullptr;
}; };
enum class SkeletonDocumentEditMode enum class SkeletonDocumentEditMode

View File

@ -193,6 +193,12 @@ void SkeletonGraphicsWidget::showContextMenu(const QPoint &pos)
contextMenu.addAction(&breakAction); contextMenu.addAction(&breakAction);
} }
QAction reverseAction(tr("Reverse"), this);
if (!m_nodePositionModifyOnly && hasEdgeSelection()) {
connect(&reverseAction, &QAction::triggered, this, &SkeletonGraphicsWidget::reverseSelectedEdges);
contextMenu.addAction(&reverseAction);
}
QAction connectAction(tr("Connect"), this); QAction connectAction(tr("Connect"), this);
if (!m_nodePositionModifyOnly && hasTwoDisconnectedNodesSelection()) { if (!m_nodePositionModifyOnly && hasTwoDisconnectedNodesSelection()) {
connect(&connectAction, &QAction::triggered, this, &SkeletonGraphicsWidget::connectSelected); connect(&connectAction, &QAction::triggered, this, &SkeletonGraphicsWidget::connectSelected);
@ -521,6 +527,23 @@ void SkeletonGraphicsWidget::breakSelected()
emit groupOperationAdded(); emit groupOperationAdded();
} }
void SkeletonGraphicsWidget::reverseSelectedEdges()
{
std::set<QUuid> edgeIds;
for (const auto &it: m_rangeSelectionSet) {
if (it->data(0) == "edge")
edgeIds.insert(((SkeletonGraphicsEdgeItem *)it)->id());
}
if (edgeIds.empty())
return;
emit batchChangeBegin();
for (const auto &it: edgeIds) {
emit reverseEdge(it);
}
emit batchChangeEnd();
emit groupOperationAdded();
}
void SkeletonGraphicsWidget::connectSelected() void SkeletonGraphicsWidget::connectSelected()
{ {
std::vector<QUuid> nodeIds; std::vector<QUuid> nodeIds;
@ -2102,6 +2125,22 @@ void SkeletonGraphicsWidget::nodeAdded(QUuid nodeId)
} }
} }
void SkeletonGraphicsWidget::edgeReversed(QUuid edgeId)
{
const SkeletonEdge *edge = m_document->findEdge(edgeId);
if (nullptr == edge) {
qDebug() << "Edge changed but edge id not exist:" << edgeId;
return;
}
auto edgeItemIt = edgeItemMap.find(edgeId);
if (edgeItemIt == edgeItemMap.end()) {
qDebug() << "Edge removed but edge id not exist:" << edgeId;
return;
}
edgeItemIt->second.first->reverse();
edgeItemIt->second.second->reverse();
}
void SkeletonGraphicsWidget::edgeAdded(QUuid edgeId) void SkeletonGraphicsWidget::edgeAdded(QUuid edgeId)
{ {
const SkeletonEdge *edge = m_document->findEdge(edgeId); const SkeletonEdge *edge = m_document->findEdge(edgeId);
@ -2257,10 +2296,6 @@ void SkeletonGraphicsWidget::nodeOriginChanged(QUuid nodeId)
} }
} }
void SkeletonGraphicsWidget::edgeChanged(QUuid edgeId)
{
}
void SkeletonGraphicsWidget::partVisibleStateChanged(QUuid partId) void SkeletonGraphicsWidget::partVisibleStateChanged(QUuid partId)
{ {
const SkeletonPart *part = m_document->findPart(partId); const SkeletonPart *part = m_document->findPart(partId);

View File

@ -429,6 +429,11 @@ public:
m_deactivated = deactivated; m_deactivated = deactivated;
updateAppearance(); updateAppearance();
} }
void reverse()
{
std::swap(m_firstItem, m_secondItem);
updateAppearance();
}
bool deactivated() bool deactivated()
{ {
return m_deactivated; return m_deactivated;
@ -477,6 +482,7 @@ signals:
void open(); void open();
void exportResult(); void exportResult();
void breakEdge(QUuid edgeId); void breakEdge(QUuid edgeId);
void reverseEdge(QUuid edgeId);
void moveOriginBy(float x, float y, float z); void moveOriginBy(float x, float y, float z);
void partChecked(QUuid partId); void partChecked(QUuid partId);
void partUnchecked(QUuid partId); void partUnchecked(QUuid partId);
@ -555,7 +561,7 @@ public slots:
void nodeRadiusChanged(QUuid nodeId); void nodeRadiusChanged(QUuid nodeId);
void nodeBoneMarkChanged(QUuid nodeId); void nodeBoneMarkChanged(QUuid nodeId);
void nodeOriginChanged(QUuid nodeId); void nodeOriginChanged(QUuid nodeId);
void edgeChanged(QUuid edgeId); void edgeReversed(QUuid edgeId);
void turnaroundChanged(); void turnaroundChanged();
void canvasResized(); void canvasResized();
void editModeChanged(); void editModeChanged();
@ -580,6 +586,7 @@ public slots:
void fadeSelected(); void fadeSelected();
void colorizeSelected(); void colorizeSelected();
void breakSelected(); void breakSelected();
void reverseSelectedEdges();
void connectSelected(); void connectSelected();
void rotateSelected(int degree); void rotateSelected(int degree);
void zoomSelected(float delta); void zoomSelected(float delta);

View File

@ -36,7 +36,7 @@ SkinnedMeshCreator::SkinnedMeshCreator(const Outcome &outcome,
} }
} }
MeshLoader *SkinnedMeshCreator::createMeshFromTransform(const std::vector<QMatrix4x4> &matricies) Model *SkinnedMeshCreator::createMeshFromTransform(const std::vector<QMatrix4x4> &matricies)
{ {
std::vector<std::vector<QVector3D>> transformedPositions = m_verticesBindPositions; std::vector<std::vector<QVector3D>> transformedPositions = m_verticesBindPositions;
std::vector<std::vector<QVector3D>> transformedPoseNormals = m_verticesBindNormals; std::vector<std::vector<QVector3D>> transformedPoseNormals = m_verticesBindNormals;
@ -78,10 +78,10 @@ MeshLoader *SkinnedMeshCreator::createMeshFromTransform(const std::vector<QMatri
currentVertex.normX = sourceNormal.x(); currentVertex.normX = sourceNormal.x();
currentVertex.normY = sourceNormal.y(); currentVertex.normY = sourceNormal.y();
currentVertex.normZ = sourceNormal.z(); currentVertex.normZ = sourceNormal.z();
currentVertex.metalness = MeshLoader::m_defaultMetalness; currentVertex.metalness = Model::m_defaultMetalness;
currentVertex.roughness = MeshLoader::m_defaultRoughness; currentVertex.roughness = Model::m_defaultRoughness;
} }
} }
return new MeshLoader(triangleVertices, triangleVerticesNum); return new Model(triangleVertices, triangleVerticesNum);
} }

View File

@ -4,7 +4,7 @@
#include <vector> #include <vector>
#include <QVector3D> #include <QVector3D>
#include <QColor> #include <QColor>
#include "meshloader.h" #include "model.h"
#include "outcome.h" #include "outcome.h"
#include "jointnodetree.h" #include "jointnodetree.h"
@ -13,7 +13,7 @@ class SkinnedMeshCreator
public: public:
SkinnedMeshCreator(const Outcome &outcome, SkinnedMeshCreator(const Outcome &outcome,
const std::map<int, RiggerVertexWeights> &resultWeights); const std::map<int, RiggerVertexWeights> &resultWeights);
MeshLoader *createMeshFromTransform(const std::vector<QMatrix4x4> &matricies); Model *createMeshFromTransform(const std::vector<QMatrix4x4> &matricies);
private: private:
Outcome m_outcome; Outcome m_outcome;
std::map<int, RiggerVertexWeights> m_resultWeights; std::map<int, RiggerVertexWeights> m_resultWeights;

View File

@ -194,7 +194,7 @@ void saveSkeletonToXmlStream(Snapshot *snapshot, QXmlStreamWriter *writer)
writer->writeEndDocument(); writer->writeEndDocument();
} }
void loadSkeletonFromXmlStream(Snapshot *snapshot, QXmlStreamReader &reader) void loadSkeletonFromXmlStream(Snapshot *snapshot, QXmlStreamReader &reader, quint32 flags)
{ {
std::stack<QString> componentStack; std::stack<QString> componentStack;
std::vector<QString> elementNameStack; std::vector<QString> elementNameStack;
@ -223,46 +223,56 @@ void loadSkeletonFromXmlStream(Snapshot *snapshot, QXmlStreamReader &reader)
//qDebug() << (reader.isStartElement() ? "<" : ">") << "fullName:" << fullName << "baseName:" << baseName; //qDebug() << (reader.isStartElement() ? "<" : ">") << "fullName:" << fullName << "baseName:" << baseName;
if (reader.isStartElement()) { if (reader.isStartElement()) {
if (fullName == "canvas") { if (fullName == "canvas") {
foreach(const QXmlStreamAttribute &attr, reader.attributes()) { if (flags & SNAPSHOT_ITEM_CANVAS) {
snapshot->canvas[attr.name().toString()] = attr.value().toString(); foreach(const QXmlStreamAttribute &attr, reader.attributes()) {
snapshot->canvas[attr.name().toString()] = attr.value().toString();
}
} }
} else if (fullName == "canvas.nodes.node") { } else if (fullName == "canvas.nodes.node") {
QString nodeId = reader.attributes().value("id").toString(); QString nodeId = reader.attributes().value("id").toString();
if (nodeId.isEmpty()) if (nodeId.isEmpty())
continue; continue;
std::map<QString, QString> *nodeMap = &snapshot->nodes[nodeId]; if (flags & SNAPSHOT_ITEM_COMPONENT) {
foreach(const QXmlStreamAttribute &attr, reader.attributes()) { std::map<QString, QString> *nodeMap = &snapshot->nodes[nodeId];
(*nodeMap)[attr.name().toString()] = attr.value().toString(); foreach(const QXmlStreamAttribute &attr, reader.attributes()) {
(*nodeMap)[attr.name().toString()] = attr.value().toString();
}
} }
} else if (fullName == "canvas.edges.edge") { } else if (fullName == "canvas.edges.edge") {
QString edgeId = reader.attributes().value("id").toString(); QString edgeId = reader.attributes().value("id").toString();
if (edgeId.isEmpty()) if (edgeId.isEmpty())
continue; continue;
std::map<QString, QString> *edgeMap = &snapshot->edges[edgeId]; if (flags & SNAPSHOT_ITEM_COMPONENT) {
foreach(const QXmlStreamAttribute &attr, reader.attributes()) { std::map<QString, QString> *edgeMap = &snapshot->edges[edgeId];
(*edgeMap)[attr.name().toString()] = attr.value().toString(); foreach(const QXmlStreamAttribute &attr, reader.attributes()) {
(*edgeMap)[attr.name().toString()] = attr.value().toString();
}
} }
} else if (fullName == "canvas.parts.part") { } else if (fullName == "canvas.parts.part") {
QString partId = reader.attributes().value("id").toString(); QString partId = reader.attributes().value("id").toString();
if (partId.isEmpty()) if (partId.isEmpty())
continue; continue;
std::map<QString, QString> *partMap = &snapshot->parts[partId]; if (flags & SNAPSHOT_ITEM_COMPONENT) {
foreach(const QXmlStreamAttribute &attr, reader.attributes()) { std::map<QString, QString> *partMap = &snapshot->parts[partId];
(*partMap)[attr.name().toString()] = attr.value().toString(); foreach(const QXmlStreamAttribute &attr, reader.attributes()) {
(*partMap)[attr.name().toString()] = attr.value().toString();
}
} }
} else if (fullName == "canvas.partIdList.partId") { } else if (fullName == "canvas.partIdList.partId") {
QString partId = reader.attributes().value("id").toString(); QString partId = reader.attributes().value("id").toString();
if (partId.isEmpty()) if (partId.isEmpty())
continue; continue;
QString componentId = QUuid::createUuid().toString(); if (flags & SNAPSHOT_ITEM_COMPONENT) {
auto &component = snapshot->components[componentId]; QString componentId = QUuid::createUuid().toString();
component["id"] = componentId; auto &component = snapshot->components[componentId];
component["linkData"] = partId; component["id"] = componentId;
component["linkDataType"] = "partId"; component["linkData"] = partId;
auto &childrenIds = snapshot->rootComponent["children"]; component["linkDataType"] = "partId";
if (!childrenIds.isEmpty()) auto &childrenIds = snapshot->rootComponent["children"];
childrenIds += ","; if (!childrenIds.isEmpty())
childrenIds += componentId; childrenIds += ",";
childrenIds += componentId;
}
} else if (fullName.startsWith("canvas.components.component")) { } else if (fullName.startsWith("canvas.components.component")) {
QString componentId = reader.attributes().value("id").toString(); QString componentId = reader.attributes().value("id").toString();
QString parentId; QString parentId;
@ -271,14 +281,16 @@ void loadSkeletonFromXmlStream(Snapshot *snapshot, QXmlStreamReader &reader)
componentStack.push(componentId); componentStack.push(componentId);
if (componentId.isEmpty()) if (componentId.isEmpty())
continue; continue;
std::map<QString, QString> *componentMap = &snapshot->components[componentId]; if (flags & SNAPSHOT_ITEM_COMPONENT) {
foreach(const QXmlStreamAttribute &attr, reader.attributes()) { std::map<QString, QString> *componentMap = &snapshot->components[componentId];
(*componentMap)[attr.name().toString()] = attr.value().toString(); foreach(const QXmlStreamAttribute &attr, reader.attributes()) {
(*componentMap)[attr.name().toString()] = attr.value().toString();
}
auto &parentChildrenIds = parentId.isEmpty() ? snapshot->rootComponent["children"] : snapshot->components[parentId]["children"];
if (!parentChildrenIds.isEmpty())
parentChildrenIds += ",";
parentChildrenIds += componentId;
} }
auto &parentChildrenIds = parentId.isEmpty() ? snapshot->rootComponent["children"] : snapshot->components[parentId]["children"];
if (!parentChildrenIds.isEmpty())
parentChildrenIds += ",";
parentChildrenIds += componentId;
} else if (fullName == "canvas.materials.material.layers.layer") { } else if (fullName == "canvas.materials.material.layers.layer") {
currentMaterialLayer = decltype(currentMaterialLayer)(); currentMaterialLayer = decltype(currentMaterialLayer)();
foreach(const QXmlStreamAttribute &attr, reader.attributes()) { foreach(const QXmlStreamAttribute &attr, reader.attributes()) {
@ -340,13 +352,19 @@ void loadSkeletonFromXmlStream(Snapshot *snapshot, QXmlStreamReader &reader)
} else if (fullName == "canvas.materials.material.layers.layer") { } else if (fullName == "canvas.materials.material.layers.layer") {
currentMaterial.second.push_back(currentMaterialLayer); currentMaterial.second.push_back(currentMaterialLayer);
} else if (fullName == "canvas.materials.material") { } else if (fullName == "canvas.materials.material") {
snapshot->materials.push_back(currentMaterial); if (flags & SNAPSHOT_ITEM_MATERIAL) {
snapshot->materials.push_back(currentMaterial);
}
} else if (fullName == "canvas.poses.pose.frames.frame") { } else if (fullName == "canvas.poses.pose.frames.frame") {
currentPose.second.push_back(currentPoseFrame); currentPose.second.push_back(currentPoseFrame);
} else if (fullName == "canvas.poses.pose") { } else if (fullName == "canvas.poses.pose") {
snapshot->poses.push_back(currentPose); if (flags & SNAPSHOT_ITEM_POSE) {
snapshot->poses.push_back(currentPose);
}
} else if (fullName == "canvas.motions.motion") { } else if (fullName == "canvas.motions.motion") {
snapshot->motions.push_back(currentMotion); if (flags & SNAPSHOT_ITEM_MOTION) {
snapshot->motions.push_back(currentMotion);
}
} }
} }
} }

View File

@ -3,7 +3,21 @@
#include <QXmlStreamWriter> #include <QXmlStreamWriter>
#include "snapshot.h" #include "snapshot.h"
#define SNAPSHOT_ITEM_CANVAS 0x00000001
#define SNAPSHOT_ITEM_COMPONENT 0x00000002
#define SNAPSHOT_ITEM_MATERIAL 0x00000004
#define SNAPSHOT_ITEM_POSE 0x00000008
#define SNAPSHOT_ITEM_MOTION 0x00000010
#define SNAPSHOT_ITEM_ALL ( \
SNAPSHOT_ITEM_CANVAS | \
SNAPSHOT_ITEM_COMPONENT | \
SNAPSHOT_ITEM_MATERIAL | \
SNAPSHOT_ITEM_POSE | \
SNAPSHOT_ITEM_MOTION \
)
void saveSkeletonToXmlStream(Snapshot *snapshot, QXmlStreamWriter *writer); void saveSkeletonToXmlStream(Snapshot *snapshot, QXmlStreamWriter *writer);
void loadSkeletonFromXmlStream(Snapshot *snapshot, QXmlStreamReader &reader); void loadSkeletonFromXmlStream(Snapshot *snapshot, QXmlStreamReader &reader,
quint32 flags=SNAPSHOT_ITEM_ALL);
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
#ifndef DUST3D_BUILDER_H #ifndef DUST3D_STOKE_MESH_BUILDER_H
#define DUST3D_BUILDER_H #define DUST3D_STOKE_MESH_BUILDER_H
#include <QVector3D> #include <QVector3D>
#include <QVector2D> #include <QVector2D>
#include <vector> #include <vector>
@ -22,8 +22,28 @@ public:
bool reverse = false; bool reverse = false;
}; };
struct Node
{
float radius;
QVector3D position;
std::vector<QVector2D> cutTemplate;
float cutRotation;
int nearOriginNodeIndex = -1;
int farOriginNodeIndex = -1;
size_t index;
std::vector<size_t> neighbors;
size_t next;
QVector3D cutNormal;
QVector3D traverseDirection;
QVector3D baseNormal;
size_t traverseOrder;
size_t nextOrNeighborOtherThan(size_t neighborIndex) const;
};
size_t addNode(const QVector3D &position, float radius, const std::vector<QVector2D> &cutTemplate, float cutRotation); size_t addNode(const QVector3D &position, float radius, const std::vector<QVector2D> &cutTemplate, float cutRotation);
size_t addEdge(size_t firstNodeIndex, size_t secondNodeIndex); void addEdge(size_t firstNodeIndex, size_t secondNodeIndex);
void setNodeOriginInfo(size_t nodeIndex, int nearOriginNodeIndex, int farOriginNodeIndex); void setNodeOriginInfo(size_t nodeIndex, int nearOriginNodeIndex, int farOriginNodeIndex);
void setDeformThickness(float thickness); void setDeformThickness(float thickness);
void setDeformWidth(float width); void setDeformWidth(float width);
@ -34,78 +54,18 @@ public:
void enableBaseNormalOnY(bool enabled); void enableBaseNormalOnY(bool enabled);
void enableBaseNormalOnZ(bool enabled); void enableBaseNormalOnZ(bool enabled);
void enableBaseNormalAverage(bool enabled); void enableBaseNormalAverage(bool enabled);
const std::vector<QVector3D> &generatedVertices(); bool buildBaseNormalsOnly();
const std::vector<std::vector<size_t>> &generatedFaces(); const std::vector<Node> &nodes() const;
const std::vector<size_t> &generatedVerticesSourceNodeIndices(); const std::vector<size_t> &nodeIndices() const;
void exportAsObj(const QString &filename);
bool build();
const CutFaceTransform *nodeAdjustableCutFaceTransform(size_t nodeIndex);
const QVector3D &nodeTraverseDirection(size_t nodeIndex) const; const QVector3D &nodeTraverseDirection(size_t nodeIndex) const;
const QVector3D &nodeBaseNormal(size_t nodeIndex) const; const QVector3D &nodeBaseNormal(size_t nodeIndex) const;
size_t nodeTraverseOrder(size_t nodeIndex) const; size_t nodeTraverseOrder(size_t nodeIndex) const;
bool build();
const std::vector<QVector3D> &generatedVertices();
const std::vector<std::vector<size_t>> &generatedFaces();
const std::vector<size_t> &generatedVerticesSourceNodeIndices();
private: private:
struct Edge;
struct Node
{
float radius;
QVector3D position;
std::vector<size_t> edges;
std::vector<QVector2D> cutTemplate;
float cutRotation;
std::vector<QVector3D> raysToNeibors;
QVector3D cutNormal;
CutFaceTransform cutFaceTransform;
QVector3D initialTraverseDirection;
QVector3D traverseDirection;
QVector3D growthDirection;
QVector3D initialBaseNormal;
QVector3D baseNormal;
size_t reversedTraverseOrder;
bool hasInitialBaseNormal = false;
bool baseNormalResolved = false;
bool baseNormalSearched = false;
bool hasInitialTraverseDirection = false;
bool hasAdjustableCutFace = false;
int nearOriginNodeIndex = -1;
int farOriginNodeIndex = -1;
size_t anotherEdge(size_t edgeIndex) const
{
if (edges.size() != 2)
return edgeIndex;
const auto &otherIndex = edges[0];
if (otherIndex == edgeIndex)
return edges[1];
return otherIndex;
}
};
struct Edge
{
std::vector<size_t> nodes;
std::vector<std::pair<std::vector<size_t>, QVector3D>> cuts;
size_t neiborOf(size_t nodeIndex) const
{
const auto &otherIndex = nodes[0];
if (otherIndex == nodeIndex)
return nodes[1];
return otherIndex;
}
void updateNodeIndex(size_t fromNodeIndex, size_t toNodeIndex)
{
if (nodes[0] == fromNodeIndex) {
nodes[0] = toNodeIndex;
return;
}
nodes[1] = toNodeIndex;
}
};
struct GeneratedVertexInfo struct GeneratedVertexInfo
{ {
size_t orderInCut; size_t orderInCut;
@ -113,16 +73,6 @@ private:
}; };
std::vector<Node> m_nodes; std::vector<Node> m_nodes;
std::vector<Edge> m_edges;
std::vector<QVector3D> m_generatedVertices;
std::vector<QVector3D> m_generatedVerticesCutDirects;
std::vector<size_t> m_generatedVerticesSourceNodeIndices;
std::vector<GeneratedVertexInfo> m_generatedVerticesInfos;
std::vector<std::vector<size_t>> m_generatedFaces;
std::vector<size_t> m_sortedNodeIndices;
std::map<size_t, size_t> m_weldMap;
std::set<size_t> m_swallowedEdges;
std::set<size_t> m_swallowedNodes;
float m_deformThickness = 1.0f; float m_deformThickness = 1.0f;
float m_deformWidth = 1.0f; float m_deformWidth = 1.0f;
float m_cutRotation = 0.0f; float m_cutRotation = 0.0f;
@ -132,48 +82,38 @@ private:
bool m_baseNormalAverageEnabled = false; bool m_baseNormalAverageEnabled = false;
const QImage *m_deformMapImage = nullptr; const QImage *m_deformMapImage = nullptr;
float m_deformMapScale = 0.0f; float m_deformMapScale = 0.0f;
float m_hollowThickness = 0.2f; float m_hollowThickness = 0.0f;
std::vector<std::vector<size_t>> m_endCuts;
void sortNodeIndices(); bool m_isRing = false;
void prepareNode(size_t nodeIndex); std::vector<size_t> m_nodeIndices;
std::pair<QVector3D, bool> calculateBaseNormal(const std::vector<QVector3D> &inputDirects, std::vector<QVector3D> m_generatedVertices;
const std::vector<QVector3D> &inputPositions, std::vector<QVector3D> m_generatedVerticesCutDirects;
const std::vector<float> &weights); std::vector<size_t> m_generatedVerticesSourceNodeIndices;
bool validateNormal(const QVector3D &normal); std::vector<GeneratedVertexInfo> m_generatedVerticesInfos;
void resolveBaseNormalRecursively(size_t nodeIndex); std::vector<std::vector<size_t>> m_generatedFaces;
void resolveBaseNormalForLeavesRecursively(size_t nodeIndex, const QVector3D &baseNormal);
std::pair<QVector3D, bool> searchBaseNormalFromNeighborsRecursively(size_t nodeIndex); bool prepare();
QVector3D revisedBaseNormalAcordingToCutNormal(const QVector3D &baseNormal, const QVector3D &cutNormal); QVector3D calculateBaseNormalFromTraverseDirection(const QVector3D &traverseDirection);
void resolveInitialTraverseDirectionRecursively(size_t nodeIndex, const QVector3D *from, std::set<size_t> *visited); std::vector<QVector3D> makeCut(const QVector3D &cutCenter,
void unifyBaseNormals(); float radius,
void localAverageBaseNormals(); const std::vector<QVector2D> &cutTemplate,
void resolveTraverseDirection(size_t nodeIndex); const QVector3D &cutNormal,
bool generateCutsForNode(size_t nodeIndex); const QVector3D &baseNormal);
bool tryWrapMultipleBranchesForNode(size_t nodeIndex, std::vector<float> &offsets, bool &offsetsChanged);
void makeCut(const QVector3D &position,
float radius,
const std::vector<QVector2D> &cutTemplate,
float cutRotation,
QVector3D &baseNormal,
QVector3D &cutNormal,
const QVector3D &traverseDirection,
std::vector<QVector3D> &resultCut,
CutFaceTransform *cutFaceTransform=nullptr,
bool *cutFlipped=nullptr);
void insertCutVertices(const std::vector<QVector3D> &cut, void insertCutVertices(const std::vector<QVector3D> &cut,
std::vector<size_t> &vertices, std::vector<size_t> *vertices,
size_t nodeIndex, size_t nodeIndex,
const QVector3D &cutDirect, const QVector3D &cutNormal);
bool cutFlipped); void buildMesh();
void stitchEdgeCuts(); std::vector<size_t> sortedNodeIndices(bool *isRing);
void applyWeld(); bool calculateStartingNodeIndex(size_t *startingNodeIndex,
bool *isRing);
void reviseTraverseDirections();
void localAverageBaseNormals();
void unifyBaseNormals();
std::vector<size_t> edgeloopFlipped(const std::vector<size_t> &edgeLoop);
void reviseNodeBaseNormal(Node &node);
static QVector3D calculateDeformPosition(const QVector3D &vertexPosition, const QVector3D &ray, const QVector3D &deformNormal, float deformFactor);
void applyDeform(); void applyDeform();
void finalizeHollow();
QVector3D calculateDeformPosition(const QVector3D &vertexPosition, const QVector3D &ray, const QVector3D &deformNormal, float deformFactor);
bool swallowEdgeForNode(size_t nodeIndex, size_t edgeOrder);
static QVector3D calculateBaseNormalFromTraverseDirection(const QVector3D &traverseDirection);
void layoutNodes();
}; };
#endif #endif

View File

@ -179,12 +179,12 @@ void StrokeModifier::finalize()
} }
} }
const std::vector<StrokeModifier::Node> &StrokeModifier::nodes() const std::vector<StrokeModifier::Node> &StrokeModifier::nodes() const
{ {
return m_nodes; return m_nodes;
} }
const std::vector<StrokeModifier::Edge> &StrokeModifier::edges() const std::vector<StrokeModifier::Edge> &StrokeModifier::edges() const
{ {
return m_edges; return m_edges;
} }

View File

@ -30,8 +30,8 @@ public:
void subdivide(); void subdivide();
void roundEnd(); void roundEnd();
void enableIntermediateAddition(); void enableIntermediateAddition();
const std::vector<Node> &nodes(); const std::vector<Node> &nodes() const;
const std::vector<Edge> &edges(); const std::vector<Edge> &edges() const;
void finalize(); void finalize();
private: private:

View File

@ -120,9 +120,9 @@ Outcome *TextureGenerator::takeOutcome()
return outcome; return outcome;
} }
MeshLoader *TextureGenerator::takeResultMesh() Model *TextureGenerator::takeResultMesh()
{ {
MeshLoader *resultMesh = m_resultMesh; Model *resultMesh = m_resultMesh;
m_resultMesh = nullptr; m_resultMesh = nullptr;
return resultMesh; return resultMesh;
} }
@ -221,7 +221,7 @@ bool TextureGenerator::hasTransparencySettings()
void TextureGenerator::generate() void TextureGenerator::generate()
{ {
m_resultMesh = new MeshLoader(*m_outcome); m_resultMesh = new Model(*m_outcome);
if (nullptr == m_outcome->triangleVertexUvs()) if (nullptr == m_outcome->triangleVertexUvs())
return; return;

View File

@ -6,7 +6,7 @@
#include <QColor> #include <QColor>
#include <QPixmap> #include <QPixmap>
#include "outcome.h" #include "outcome.h"
#include "meshloader.h" #include "model.h"
#include "snapshot.h" #include "snapshot.h"
class TextureGenerator : public QObject class TextureGenerator : public QObject
@ -25,7 +25,7 @@ public:
QImage *takeResultTextureMetalnessImage(); QImage *takeResultTextureMetalnessImage();
QImage *takeResultTextureAmbientOcclusionImage(); QImage *takeResultTextureAmbientOcclusionImage();
Outcome *takeOutcome(); Outcome *takeOutcome();
MeshLoader *takeResultMesh(); Model *takeResultMesh();
bool hasTransparencySettings(); bool hasTransparencySettings();
void addPartColorMap(QUuid partId, const QImage *image, float tileScale); void addPartColorMap(QUuid partId, const QImage *image, float tileScale);
void addPartNormalMap(QUuid partId, const QImage *image, float tileScale); void addPartNormalMap(QUuid partId, const QImage *image, float tileScale);
@ -52,7 +52,7 @@ private:
QImage *m_resultTextureRoughnessImage; QImage *m_resultTextureRoughnessImage;
QImage *m_resultTextureMetalnessImage; QImage *m_resultTextureMetalnessImage;
QImage *m_resultTextureAmbientOcclusionImage; QImage *m_resultTextureAmbientOcclusionImage;
MeshLoader *m_resultMesh; Model *m_resultMesh;
std::map<QUuid, std::pair<QImage, float>> m_partColorTextureMap; std::map<QUuid, std::pair<QImage, float>> m_partColorTextureMap;
std::map<QUuid, std::pair<QImage, float>> m_partNormalTextureMap; std::map<QUuid, std::pair<QImage, float>> m_partNormalTextureMap;
std::map<QUuid, std::pair<QImage, float>> m_partMetalnessTextureMap; std::map<QUuid, std::pair<QImage, float>> m_partMetalnessTextureMap;

View File

@ -13,8 +13,8 @@ typedef CGAL::Surface_mesh<InexactKernel::Point_3> InexactMesh;
bool triangulateFacesWithoutKeepVertices(std::vector<QVector3D> &vertices, const std::vector<std::vector<size_t>> &faces, std::vector<std::vector<size_t>> &triangles) bool triangulateFacesWithoutKeepVertices(std::vector<QVector3D> &vertices, const std::vector<std::vector<size_t>> &faces, std::vector<std::vector<size_t>> &triangles)
{ {
auto cgalMesh = buildCgalMesh<InexactKernel>(vertices, faces); auto cgalMesh = buildCgalMesh<InexactKernel>(vertices, faces);
bool isSucceed = CGAL::Polygon_mesh_processing::triangulate_faces(*cgalMesh); bool isSuccessful = CGAL::Polygon_mesh_processing::triangulate_faces(*cgalMesh);
if (isSucceed) { if (isSuccessful) {
vertices.clear(); vertices.clear();
fetchFromCgalMesh<InexactKernel>(cgalMesh, vertices, triangles); fetchFromCgalMesh<InexactKernel>(cgalMesh, vertices, triangles);
delete cgalMesh; delete cgalMesh;
@ -24,7 +24,7 @@ bool triangulateFacesWithoutKeepVertices(std::vector<QVector3D> &vertices, const
// fallback to our own imeplementation // fallback to our own imeplementation
isSucceed = true; isSuccessful = true;
std::vector<std::vector<size_t>> rings; std::vector<std::vector<size_t>> rings;
for (const auto &face: faces) { for (const auto &face: faces) {
if (face.size() > 3) { if (face.size() > 3) {
@ -79,8 +79,8 @@ bool triangulateFacesWithoutKeepVertices(std::vector<QVector3D> &vertices, const
triangles.push_back(newFace); triangles.push_back(newFace);
} else { } else {
qDebug() << "Triangulate failed, ring size:" << fillRing.size(); qDebug() << "Triangulate failed, ring size:" << fillRing.size();
isSucceed = false; isSuccessful = false;
} }
} }
return isSucceed; return isSuccessful;
} }

View File

@ -1,3 +1,4 @@
#include <QGuiApplication>
#include "turnaroundloader.h" #include "turnaroundloader.h"
TurnaroundLoader::TurnaroundLoader(const QString &filename, QSize viewSize) : TurnaroundLoader::TurnaroundLoader(const QString &filename, QSize viewSize) :
@ -33,5 +34,6 @@ void TurnaroundLoader::process()
} else { } else {
m_resultImage = new QImage(m_inputImage.scaled(m_viewSize, Qt::KeepAspectRatio)); m_resultImage = new QImage(m_inputImage.scaled(m_viewSize, Qt::KeepAspectRatio));
} }
this->moveToThread(QGuiApplication::instance()->thread());
emit finished(); emit finished();
} }

View File

@ -1,4 +1,5 @@
#include <QXmlStreamReader> #include <QXmlStreamReader>
#include <QGuiApplication>
#include "updateschecker.h" #include "updateschecker.h"
#include "version.h" #include "version.h"
@ -143,6 +144,6 @@ void UpdatesChecker::downloadFinished(QNetworkReply *reply)
} }
} }
reply->deleteLater(); reply->deleteLater();
this->moveToThread(QGuiApplication::instance()->thread());
emit finished(); emit finished();
} }