Documentation

Everything you need to integrate MeshQA into your workflow

Integrations

Integrate MeshQA with your development tools and workflows.

GitHub Actions

Validate assets in your CI/CD pipeline:

.github/workflows/validate-assets.yml
name: Validate 3D Assets

on:
  push:
    paths:
      - 'Assets/**/*.glb'
      - 'Assets/**/*.fbx'
  pull_request:
    paths:
      - 'Assets/**/*.glb'
      - 'Assets/**/*.fbx'

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install MeshQA CLI
        run: npm install -g @meshqa/cli

      - name: Validate Assets
        env:
          MESHQA_API_KEY: ${{ secrets.MESHQA_API_KEY }}
        run: |
          meshqa validate ./Assets \
            --recursive \
            --min-score 50 \
            --format json \
            --output validation-report.json

      - name: Upload Report
        uses: actions/upload-artifact@v4
        with:
          name: validation-report
          path: validation-report.json

Unity Editor Integration

Create an editor script to validate assets on import:

Editor/MeshQAValidator.cs
using UnityEditor;
using UnityEngine;
using System.Net.Http;
using System.Threading.Tasks;

public class MeshQAValidator : AssetPostprocessor
{
    private static readonly HttpClient client = new HttpClient();
    private const string API_KEY = "mqk_your_api_key"; // Use env var in production

    void OnPreprocessModel()
    {
        // Get the imported asset path
        string assetPath = assetImporter.assetPath;

        // Skip if already validated
        if (PlayerPrefs.HasKey("meshqa_" + assetPath))
            return;

        // Validate async (simplified example)
        ValidateAsset(assetPath);
    }

    async void ValidateAsset(string path)
    {
        using var content = new MultipartFormDataContent();
        var fileBytes = System.IO.File.ReadAllBytes(path);
        content.Add(new ByteArrayContent(fileBytes), "file", System.IO.Path.GetFileName(path));

        client.DefaultRequestHeaders.Authorization =
            new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", API_KEY);

        var response = await client.PostAsync(
            "https://meshqa.vercel.app/api/v1/validate",
            content
        );

        if (response.IsSuccessStatusCode)
        {
            Debug.Log($"MeshQA: Validation started for {path}");
        }
    }
}

Blender Add-on

Python script for Blender integration:

meshqa_addon.py
import bpy
import requests
import tempfile
import os

class MeshQAValidateOperator(bpy.types.Operator):
    bl_idname = "meshqa.validate"
    bl_label = "Validate with MeshQA"

    def execute(self, context):
        # Export current scene to temp GLB
        temp_path = os.path.join(tempfile.gettempdir(), "meshqa_export.glb")
        bpy.ops.export_scene.gltf(filepath=temp_path, export_format='GLB')

        # Upload to MeshQA
        api_key = bpy.context.preferences.addons['meshqa'].preferences.api_key

        with open(temp_path, 'rb') as f:
            response = requests.post(
                'https://meshqa.vercel.app/api/v1/validate',
                headers={'Authorization': f'Bearer {api_key}'},
                files={'file': f}
            )

        if response.status_code == 202:
            self.report({'INFO'}, f"Validation started: {response.json()['job_id']}")
        else:
            self.report({'ERROR'}, f"Validation failed: {response.text}")

        # Cleanup
        os.remove(temp_path)

        return {'FINISHED'}

def register():
    bpy.utils.register_class(MeshQAValidateOperator)

def unregister():
    bpy.utils.unregister_class(MeshQAValidateOperator)

Node.js/TypeScript

Programmatic validation in your Node.js application:

validate.ts
import fs from 'fs';
import FormData from 'form-data';

const API_KEY = process.env.MESHQA_API_KEY;
const BASE_URL = 'https://meshqa.vercel.app/api/v1';

async function validateAsset(filePath: string) {
  const formData = new FormData();
  formData.append('file', fs.createReadStream(filePath));
  formData.append('options', JSON.stringify({ engine: 'unity' }));

  // Start validation
  const response = await fetch(`${BASE_URL}/validate`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${API_KEY}`,
    },
    body: formData,
  });

  const { job_id } = await response.json();
  console.log(`Validation started: ${job_id}`);

  // Poll for results
  let result;
  while (true) {
    const statusRes = await fetch(`${BASE_URL}/validate/${job_id}`, {
      headers: { 'Authorization': `Bearer ${API_KEY}` },
    });
    result = await statusRes.json();

    if (result.status === 'completed' || result.status === 'failed') {
      break;
    }

    await new Promise(resolve => setTimeout(resolve, 1000));
  }

  return result;
}

// Usage
validateAsset('./model.glb').then(result => {
  console.log(`Health Score: ${result.health_score}`);
  console.log(`Grade: ${result.grade}`);
});

Webhooks for Automation

Set up webhooks to trigger actions when validations complete:

webhook-handler.ts
import express from 'express';
import crypto from 'crypto';

const app = express();
app.use(express.json());

app.post('/meshqa-webhook', (req, res) => {
  // Verify signature
  const signature = req.headers['x-meshqa-signature'];
  const payload = JSON.stringify(req.body);
  const expectedSig = crypto
    .createHmac('sha256', process.env.WEBHOOK_SECRET)
    .update(payload)
    .digest('hex');

  if (signature !== expectedSig) {
    return res.status(401).send('Invalid signature');
  }

  // Process the webhook
  const { event, job_id, result } = req.body;

  if (event === 'validation.completed') {
    if (result.health_score < 50) {
      // Send alert for low-quality assets
      notifyTeam(`Asset ${job_id} scored ${result.health_score}/100`);
    }
  }

  res.status(200).send('OK');
});

app.listen(3000);
MeshQA - 3D Asset Validation Service