-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathaction.yml
281 lines (260 loc) · 11.5 KB
/
action.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
name: "OCP Action"
author: "Yeicor"
description: "GitHub Action that builds OCP models (CadQuery/Build123d/...), renders them and sets up a model viewer on Github Pages."
branding:
icon: "package"
color: "yellow"
inputs:
scripts:
description: "The |-separated scripts containing the model(s) to build."
required: true
default: "main.py"
formats:
description: "The |-separated formats in which to build all model(s)."
required: false
default: "STL|STEP|AMF|SVG|TJS|DXF|VRML|VTP|3MF|GLTF"
tolerance:
description: "The tolerance to use when building the model(s)."
required: false
default: "0.1"
angular_tolerance:
description: "The angular tolerance to use when building the model(s)."
required: false
default: "0.1"
y-up:
description: "If true, all model(s) are converted from Z-up to Y-up."
required: false
default: "false"
website:
description: "Relative path where a static website to visualize the models using GitHub Pages will be built. Models and renders will be available at <website>/<models>/... Disabled if empty."
required: false
default: "."
website-screenshot:
description: "Whether to render the model(s) as a screenshot of the website."
required: false
default: "true"
outputs:
models-folder:
description: "The root folders containing all built models, including the website if built."
value: "${{ steps.build.outputs.models-folder }}"
runs:
using: "composite"
steps:
- name: "Install CadQuery for exporting OCP models"
shell: "bash"
run: "pip install -r ${{ github.action_path }}/requirements.txt"
- name: "Build model(s)"
id: "build"
shell: "bash"
run: |
set -ex
# Create a workspace for the models
export TMPDIR=$PWD
base_folder=$(mktemp -d)
chmod -R +rX "$base_folder" # Fix permissions
all_model_folders=""
# For each script...
for script in $(echo "${{ inputs.scripts }}" | tr "|" "\n"); do
# Copy the library to the script's folder
cp "${{ github.action_path }}/__ocp_action_api.py" "$(dirname "$script")"
# Inject our import to define the show() function
sed -i '1s/^/from __ocp_action_api import *\n/' $script
# Define environment variables for the script
export OCP_ACTION_OUT_DIR="$base_folder/$(realpath --relative-to="$GITHUB_WORKSPACE" "$(dirname "$script")")/$(basename "$script" .py)"
export OCP_ACTION_DEF_NAME="$(basename "$script" .py)"
export OCP_ACTION_WANTED_FORMATS="${{ inputs.formats }}"
export OCP_ACTION_TOLERANCE="${{ inputs.tolerance }}"
export OCP_ACTION_ANGULAR_TOLERANCE="${{ inputs.angular_tolerance }}"
export OCP_ACTION_Y_UP="${{ inputs.y-up }}"
# Build the model(s)
pushd "$(dirname "$script")"
python "$(basename "$script")"
popd
# Save the folder
all_model_folders="$all_model_folders|$OCP_ACTION_OUT_DIR"
done
# Remove the first | from the list
all_model_folders=$(echo "$all_model_folders" | cut -c2-)
# Set the output as the base folder
echo "models-folder=$base_folder" >> "$GITHUB_OUTPUT"
- name: "Upload artifacts"
uses: "actions/upload-artifact@v3"
with:
name: "Built models"
path: "${{ steps.build.outputs.models-folder }}"
- name: "Build and render website"
if: "${{ inputs.website != '' }}"
id: "website"
shell: "bash"
run: |
set -ex
# Copy all models to the website folder, to provide persistent links to latest models
export TMPDIR=$PWD
website_root_folder="$(mktemp -d)"
chmod -R +rX "$website_root_folder" # Fix permissions
website_folder="$(realpath "$website_root_folder/${{ inputs.website }}")"
mkdir -p "$website_folder/models"
cp -r "${{ steps.build.outputs.models-folder }}/"* "$website_folder/models"
# If taking screenshots, use them as posters instead of plans
poster_extension="svg"
if [ "${{ inputs.website-screenshot }}" = "true" ]; then
poster_extension="png"
fi
# Get all available glTF models as relative paths (sorted by modification date), and generate the HTML for them
gltf_models=$(cd "$website_folder" && find -name "*.gltf" -printf "%T@\t%p\n" | sort -n | cut -f 2-)
models_html=""
first_model_path=""
first_model_poster_path=""
for model_path in $gltf_models; do
model_name=$(basename "$model_path" .gltf)
model_poster_path="$(echo "$model_path" | sed "s,.gltf,.$poster_extension,g")"
selected=""
if [ "$models_html" = "" ]; then
selected=" selected"
first_model_path="$model_path"
first_model_poster_path="$model_poster_path"
fi
models_html="$models_html<button class=\"slide$selected\" onclick=\"switchSrc(this, '$model_path')\" style=\"background-image: url('$model_poster_path');\"></button>"
done
# Build a website for all models
cat >"$website_folder/index.html" <<EOF
<!DOCTYPE html>
<html>
<!-- Based on https://modelviewer.dev/examples/augmentedreality/ -->
<head>
<title>3D Models</title>
<script type="module" src="https://unpkg.com/@google/model-viewer/dist/model-viewer.min.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<style>
model-viewer { width: 100vw; height: 100vh; }
body { margin: 0; overflow: hidden; }
</style>
</head>
<body>
<model-viewer
src="${first_model_path}" poster="${first_model_poster_path}"
ar ar-modes="webxr scene-viewer quick-look" camera-controls shadow-intensity="1"
auto-rotate camera-orbit="-20deg 70deg auto" field-of-view="30deg"
environment-image="https://dl.polyhaven.org/file/ph-assets/HDRIs/hdr/1k/rural_asphalt_road_1k.hdr"
skybox-image="https://dl.polyhaven.org/file/ph-assets/HDRIs/hdr/1k/rural_asphalt_road_1k.hdr">
<effect-composer render-mode="quality">
<ssao-effect></ssao-effect>
<smaa-effect></smaa-effect>
</effect-composer>
<div class="slider">
<div class="slides">
<!--<button class="slide selected" onclick="switchSrc(this, '.../Chair.gltf')" style="background-image: url('.../Chair.webp');"></button>-->
<!--<button class="slide" onclick="switchSrc(this, '.../Mixer.gltf')" style="background-image: url('.../Mixer.webp');"></button>-->
${models_html}
</div>
</div>
</model-viewer>
<script type="module">
const modelViewer = document.querySelector("model-viewer");
window.switchSrc = (element, path) => {
// const path = "../../assets/ShopifyModels/" + name;
modelViewer.src = path;
modelViewer.poster = path.replace(".gltf", ".$poster_extension");
const slides = document.querySelectorAll(".slide");
slides.forEach((element) => {element.classList.remove("selected");});
element.classList.add("selected");
};
// If a model is given in the URL, load it
const urlParams = new URLSearchParams(window.location.search);
const model = urlParams.get("model");
if (model) {
document.querySelectorAll(".slide").forEach((element) => {
if (element.getAttribute("onclick").includes(model)) {
window.switchSrc(element, model);
}
});
}
const noSlider = urlParams.get("noSlider");
if (noSlider) {
document.querySelector(".slider").style.display = "none";
}
</script>
<style>
/* This keeps child nodes hidden while the element loads */
:not(:defined) > * {
display: none;
}
model-viewer {
background-color: #eee;
overflow-x: hidden;
}
@keyframes circle {
from { transform: translateX(-50%) rotate(0deg) translateX(50px) rotate(0deg); }
to { transform: translateX(-50%) rotate(360deg) translateX(50px) rotate(-360deg); }
}
@keyframes elongate {
from { transform: translateX(100px); }
to { transform: translateX(-100px); }
}
.slider {
width: 100%;
text-align: center;
overflow: hidden;
position: absolute;
bottom: 16px;
}
.slides {
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
scroll-behavior: smooth;
-webkit-overflow-scrolling: touch;
}
.slide {
scroll-snap-align: start;
flex-shrink: 0;
width: 100px;
height: 100px;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
background-color: #fff;
margin-right: 10px;
border-radius: 10px;
border: none;
display: flex;
}
.slide.selected {
border: 2px solid #4285f4;
}
.slide:focus {
outline: none;
}
.slide:focus-visible {
outline: 1px solid #4285f4;
}
</style>
</body>
</html>
EOF
# Generate a screenshot of the static site for each of the models
if [ "${{ inputs.website-screenshot }}" = "true" ]; then
sudo apt-get install chromium-browser
find $website_folder
for model_path in $gltf_models; do
model_png_path="$(echo "$model_path" | sed 's,.gltf,.png,g')"
chromium-browser --headless --disable-gpu --disable-web-security --user-data-dir="$website_folder/__chrome" \
--headless=new --virtual-time-budget=2500 --screenshot="test.png" \
"file://$website_folder/index.html?model=$model_path&noSlider=true"
mv "test.png" "$website_folder/$model_png_path"
done
rm -rf "$website_folder/__chrome"
fi
# Copy website content to the models folder
cp -r "$website_folder/" "${{ steps.build.outputs.models-folder }}"
# Export the website root folder, fixing permissions first
echo "website-folder=$website_root_folder" >> "$GITHUB_OUTPUT"
# Boilerplate to deploy the website to GitHub Pages:
- name: "Upload website as an artifact"
if: "${{ inputs.website != '' }}"
uses: "actions/upload-pages-artifact@v2"
with:
path: "${{ steps.website.outputs.website-folder }}"
- name: "Deploy website artifact to GitHub Pages"
if: "${{ inputs.website != '' }}"
uses: "actions/deploy-pages@v2"