Asset Catalog Generation
Create visual catalogs of 3D assets with auto-generated thumbnails and metadata.
Workflow Overview
Asset Library
3D Models
Processing
Translate to SVF2🚀SVF2Current APS viewing format (improved performance).View in glossary
Generate Thumbnails
Extract Metadata
Catalog Generation
Create Index
Build Grid View
Add Search
Output
HTML Gallery
JSON📋JSONStandard data interchange format.View in glossary API🔌APIInterface for software components to communicate.View in glossary
Static Site
CLI Approach
Step 1: Generate Thumbnails
mkdir -p ./catalog/thumbnails
raps object list media-assets --output json | jq -r '.[].key' | while read key; do
URN=$(raps object urn media-assets "$key" --output plain)
# Check if translated
STATUS=$(raps translate manifest "$URN" 2>/dev/null | jq -r '.status // "none"')
if [ "$STATUS" = "success" ]; then
SAFE_NAME=$(echo "$key" | tr '/' '_' | sed 's/\.[^.]*$//')
raps derivative thumbnail "$URN" --output "./catalog/thumbnails/${SAFE_NAME}.png"
echo "Generated: ${SAFE_NAME}.png"
fi
done
Step 2: Extract Metadata
echo "[]" > ./catalog/assets.json
raps object list media-assets --output json | jq -r '.[].key' | while read key; do
URN=$(raps object urn media-assets "$key" --output plain)
STATUS=$(raps translate manifest "$URN" 2>/dev/null | jq -r '.status // "none"')
if [ "$STATUS" = "success" ]; then
SAFE_NAME=$(echo "$key" | tr '/' '_' | sed 's/\.[^.]*$//')
# Get metadata
META=$(raps derivative metadata "$URN" --output json 2>/dev/null || echo '{}')
# Add to catalog
jq --arg key "$key" \
--arg name "$SAFE_NAME" \
--argjson meta "$META" \
'. += [{
key: $key,
name: $name,
thumbnail: ($name + ".png"),
metadata: $meta
}]' ./catalog/assets.json > ./catalog/assets.tmp.json
mv ./catalog/assets.tmp.json ./catalog/assets.json
fi
done
Step 3: Generate HTML Catalog
cat > ./catalog/index.html << 'EOF'
<!DOCTYPE html>
<html>
<head>
<title>3D Asset Catalog</title>
<style>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; margin: 0; padding: 20px; background: #1a1a2e; color: #eee; }
h1 { text-align: center; }
.search { width: 100%; padding: 12px; font-size: 16px; margin-bottom: 20px; border-radius: 8px; border: none; }
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 20px; }
.asset { background: #16213e; border-radius: 12px; overflow: hidden; transition: transform 0.2s; }
.asset:hover { transform: scale(1.02); }
.asset img { width: 100%; height: 180px; object-fit: contain; background: #0f3460; }
.asset-info { padding: 12px; }
.asset-name { font-weight: 600; margin-bottom: 4px; }
.asset-meta { font-size: 12px; color: #888; }
.tags { display: flex; gap: 4px; flex-wrap: wrap; margin-top: 8px; }
.tag { background: #0f3460; padding: 2px 8px; border-radius: 4px; font-size: 11px; }
</style>
</head>
<body>
<h1>3D Asset Catalog</h1>
<input type="text" class="search" placeholder="Search assets..." onkeyup="filter(this.value)">
<div class="grid" id="grid"></div>
<script>
let assets = [];
fetch('assets.json')
.then(r => r.json())
.then(data => { assets = data; render(assets); });
function render(data) {
document.getElementById('grid').innerHTML = data.map(a => `
<div class="asset" data-name="${a.name.toLowerCase()}">
<img src="thumbnails/${a.thumbnail}" alt="${a.name}" onerror="this.src='placeholder.png'">
<div class="asset-info">
<div class="asset-name">${a.name}</div>
<div class="asset-meta">${a.key}</div>
<div class="tags">
${(a.metadata.format || []).map(f => `<span class="tag">${f}</span>`).join('')}
</div>
</div>
</div>
`).join('');
}
function filter(query) {
const q = query.toLowerCase();
const filtered = assets.filter(a => a.name.toLowerCase().includes(q) || a.key.toLowerCase().includes(q));
render(filtered);
}
</script>
</body>
</html>
EOF
CI/CD Pipeline
# .github/workflows/asset-catalog.yml
name: Asset Catalog Generation
on:
schedule:
- cron: '0 3 * * *' # Daily at 3 AM
workflow_dispatch:
env:
BUCKET: media-assets
jobs:
generate-catalog:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install RAPS
run: cargo install raps
- name: Generate thumbnails
env:
APS_CLIENT_ID: ${{ secrets.APS_CLIENT_ID }}
APS_CLIENT_SECRET: ${{ secrets.APS_CLIENT_SECRET }}
run: |
mkdir -p ./catalog/thumbnails
raps object list "$BUCKET" --output json | jq -r '.[].key' | while read key; do
URN=$(raps object urn "$BUCKET" "$key" --output plain)
STATUS=$(raps translate manifest "$URN" 2>/dev/null | jq -r '.status // "none"')
if [ "$STATUS" = "success" ]; then
SAFE=$(echo "$key" | tr '/' '_' | sed 's/\.[^.]*$//')
raps derivative thumbnail "$URN" --output "./catalog/thumbnails/${SAFE}.png" 2>/dev/null || true
fi
done
- name: Generate catalog data
env:
APS_CLIENT_ID: ${{ secrets.APS_CLIENT_ID }}
APS_CLIENT_SECRET: ${{ secrets.APS_CLIENT_SECRET }}
run: |
echo "[]" > ./catalog/assets.json
raps object list "$BUCKET" --output json | jq -r '.[].key' | while read key; do
URN=$(raps object urn "$BUCKET" "$key" --output plain)
STATUS=$(raps translate manifest "$URN" 2>/dev/null | jq -r '.status // "none"')
if [ "$STATUS" = "success" ]; then
SAFE=$(echo "$key" | tr '/' '_' | sed 's/\.[^.]*$//')
META=$(raps derivative metadata "$URN" --output json 2>/dev/null | jq -c '.' || echo '{}')
jq --arg k "$key" --arg n "$SAFE" --argjson m "$META" \
'. += [{key:$k, name:$n, thumbnail:($n+".png"), metadata:$m}]' \
./catalog/assets.json > tmp.json && mv tmp.json ./catalog/assets.json
fi
done
- name: Create HTML
run: |
# Generate index.html (see CLI section)
echo "HTML catalog generated"
- name: Deploy to Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./catalog
Pipeline Flow
Daily 3 AM
Schedule
→
Generation
List Assets
Generate Thumbnails
Extract Metadata
Build HTML
→
Deploy
GitHub Pages
MCP Integration
Generate Catalog
User: "Create a visual catalog of all our 3D assets"
User
AI Assistant
RAPS🌼RAPSRust CLI for Autodesk Platform Services.View in glossary MCP🧠MCPProtocol for AI assistant tool integration.View in glossary
APS☁️APSAutodesk Platform Services - cloud APIs for CAD/BIM automation.View in glossary Cloud
User→AIGenerate asset catalog
MCP→APSList objects
APS←MCPObject list
loopAI→MCPaps_derivative_metadata(urn)
AI→AIGenerate HTML gallery
AI→UserCatalog ready
AI Response:
Generated asset catalog with 78 items:
Categories:
- Characters: 12
- Environments: 8
- Props: 45
- Vehicles: 13
Formats:
- FBX🎬FBXAutodesk's 3D animation exchange format.View in glossary: 52
- OBJ🎲OBJSimple 3D geometry format.View in glossary: 18
- glTF: 8
Search Catalog
User: "Find all vehicle assets"
AI Response:
Found 13 vehicle assets:
Name Format Polygons sports_car_01 FBX 45,000 truck_pickup FBX 32,000 motorcycle OBJ 18,000 bicycle glTF 8,500 … … … Would you like to see thumbnails?
Advanced Features
Category Organization
# Organize by file path/type
cat ./catalog/assets.json | jq '
group_by(.key | split("/")[0]) |
map({
category: .[0].key | split("/")[0],
count: length,
assets: .
})'
Search Index
# Create search-friendly index
cat ./catalog/assets.json | jq '[.[] | {
name: .name,
key: .key,
searchText: "\(.name) \(.key) \(.metadata | tostring)"
}]' > ./catalog/search-index.json
Lazy Loading
// In catalog HTML - load thumbnails on scroll
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img);
}
});
});
document.querySelectorAll('img[data-src]').forEach(img => observer.observe(img));