Integrations
Integrate MeshQA with your development tools and workflows.
GitHub Actions
Validate assets in your CI/CD pipeline:
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.jsonUnity Editor Integration
Create an editor script to validate assets on import:
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:
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:
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:
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);