Ця сторінка ще не перекладена українською. Показано англійську версію. Переглянути англійською

Генерація каталогу ресурсів

Створення візуальних каталогів 3D-ресурсів з автоматично згенерованими мініатюрами та метаданими.

Огляд робочого процесу

Бібліотека ресурсів
3D-моделі
Обробка
Трансляція в SVF2
Генерація мініатюр
Витягнення метаданих
Генерація каталогу
Створення індексу
Побудова сітки
Додавання пошуку
Результат
HTML-галерея
JSON API
Статичний сайт

Підхід CLI

Крок 1: Генерація мініатюр

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

Крок 2: Витягнення метаданих

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

Крок 3: Генерація HTML-каталогу

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

# .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

Потік конвеєра

Щодня о 3:00
Розклад
Генерація
Перелік ресурсів
Генерація мініатюр
Витягнення метаданих
Побудова HTML
Розгортання
GitHub Pages

Інтеграція MCP

Генерація каталогу

User: "Create a visual catalog of all our 3D assets"
Користувач
AI-асистент
RAPS MCP
APS Cloud
Користувач→AIЗгенерувати каталог ресурсів
AI→MCPaps_object_list(bucket)
MCP→APSПерелік об’єктів
APS←MCPСписок об’єктів
loopAI→MCPaps_derivative_thumbnail(urn)
loopAI→MCPaps_derivative_metadata(urn)
AI→AIГенерація HTML-галереї
AI→КористувачКаталог готовий

Відповідь AI:

Згенеровано каталог ресурсів з 78 елементами:

Категорії:

  • Персонажі: 12
  • Середовища: 8
  • Реквізит: 45
  • Транспорт: 13

Формати:

  • FBX: 52
  • OBJ: 18
  • glTF: 8

Переглянути каталог

Пошук у каталозі

User: "Find all vehicle assets"

Відповідь AI:

Знайдено 13 ресурсів транспорту:

НазваФорматПолігони
sports_car_01FBX45,000
truck_pickupFBX32,000
motorcycleOBJ18,000
bicycleglTF8,500
………

Бажаєте переглянути мініатюри?


Додаткові можливості

Організація за категоріями

# Organize by file path/type
cat ./catalog/assets.json | jq '
  group_by(.key | split("/")[0]) |
  map({
    category: .[0].key | split("/")[0],
    count: length,
    assets: .
  })'

Пошуковий індекс

# Create search-friendly index
cat ./catalog/assets.json | jq '[.[] | {
  name: .name,
  key: .key,
  searchText: "\(.name) \(.key) \(.metadata | tostring)"
}]' > ./catalog/search-index.json

Ліниве завантаження

// 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));

Пов’язане