added engine prefixes schema + blender plugin
This commit is contained in:
parent
b1043733d4
commit
17bc4bb9ea
3 changed files with 1302 additions and 0 deletions
21
schema/tool/blender-xrfragments.awk
Executable file
21
schema/tool/blender-xrfragments.awk
Executable file
|
|
@ -0,0 +1,21 @@
|
|||
#!/usr/bin/env -S awk -f
|
||||
BEGIN {
|
||||
REPLACE=1
|
||||
state=0
|
||||
while( (getline < "./tool/blender-xrfragments.py") > 0 ){
|
||||
if( $1 ~ /^}/ && state == REPLACE ) state=0
|
||||
|
||||
print $0
|
||||
|
||||
if( state == REPLACE ){
|
||||
while( (getline < "./xrfragment-engine-prefixes.json") > 0 ){
|
||||
gsub("true","True",$0)
|
||||
gsub("false","False",$0)
|
||||
if( $1 == "\"$version\":" ) $2 = ($2+1)"," # bump version
|
||||
if( $0 !~ /^[{}]/ ) print $0
|
||||
}
|
||||
}
|
||||
|
||||
if( $1 == "SCHEMA" ) state=REPLACE
|
||||
}
|
||||
}
|
||||
812
schema/tool/blender-xrfragments.py
Normal file
812
schema/tool/blender-xrfragments.py
Normal file
|
|
@ -0,0 +1,812 @@
|
|||
import bpy
|
||||
import json
|
||||
|
||||
# --- 1. SCHEMA ---
|
||||
SCHEMA = {
|
||||
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://xrfragment.org/schema/xrfragment-engine-prefixes.json",
|
||||
"$version": 35,
|
||||
"title": "XR Fragments Engine Prefixes",
|
||||
"type": "object",
|
||||
"flatten": True,
|
||||
|
||||
"properties": {
|
||||
"engine": {
|
||||
"type": "enum",
|
||||
"enum": [
|
||||
["NONE", "-- Select --", ""],
|
||||
["JANUSXR", "JanusXR", ""],
|
||||
["THREEJS", "Three.js", ""],
|
||||
["GODOT3", "Godot 3", ""],
|
||||
["GODOT4", "Godot 4", ""],
|
||||
["AFRAME", "AFRAME", ""],
|
||||
["BABYLON", "Babylon.js", ""],
|
||||
["PLAYCANVAS", "PlayCanvas", ""]
|
||||
],
|
||||
"default": "NONE"
|
||||
},
|
||||
"engine_settings": {
|
||||
"oneOf": [
|
||||
{"engine_val": "JANUSXR", "$ref": "#/$defs/janus", "title": "JanusXR"},
|
||||
{"engine_val": "THREEJS", "$ref": "#/$defs/three", "title": "THREE.js"},
|
||||
{"engine_val": "GODOT3", "$ref": "#/$defs/godot3", "title": "Godot3 (beta)"},
|
||||
{"engine_val": "GODOT4", "$ref": "#/$defs/godot4", "title": "Godot4 (beta)"},
|
||||
{"engine_val": "AFRAME", "$ref": "#/$defs/aframe", "title": "AFRAME (beta)"},
|
||||
{"engine_val": "BABYLON", "$ref": "#/$defs/babylon", "title": "Babylon.js (beta)"},
|
||||
{"engine_val": "PLAYCANVAS", "$ref": "#/$defs/playcanvas", "title": "PlayCanvas (beta)"}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
"$defs": {
|
||||
|
||||
|
||||
"three": {
|
||||
"oneOf":[
|
||||
{"$ref": "#/$defs/three_material"},
|
||||
{"$ref": "#/$defs/three_object"}
|
||||
]
|
||||
},
|
||||
|
||||
"aframe": {
|
||||
"oneOf":[
|
||||
{"$ref": "#/$defs/three_material"},
|
||||
{"$ref": "#/$defs/three_object"}
|
||||
]
|
||||
},
|
||||
|
||||
"three_material":{
|
||||
"title": "Material",
|
||||
"description": "Material is THREE's abstract base material class",
|
||||
"url": "https://threejs.org/docs/?q=material#Material",
|
||||
"properties":{
|
||||
"_three-type": { "title": "type", "type": "string", "default":"material", "description": "needed for blender UI"},
|
||||
"-three-material.blending":{
|
||||
"type":"string",
|
||||
"enum": ["THREE.NormalBlending", "THREE.NoBlending", "THREE.AdditiveBlending", "THREE.SubtractiveBlending", "THREE.MultiplyBlending" ]
|
||||
},
|
||||
"-three-material.sides":{
|
||||
"type":"string",
|
||||
"description": "culling: which sides to render (less increases fps)",
|
||||
"enum": ["THREE.FrontSide","THREE.BackSide","THREE.DoubleSide"]
|
||||
},
|
||||
"-three-material.map.offset.x+":{
|
||||
"type":"number",
|
||||
"description": "texture scrolling speed",
|
||||
"default": 0.005,
|
||||
"minimum": 0.000001,
|
||||
"maximum": 1
|
||||
},
|
||||
"-three-material.map.offset.yx+":{
|
||||
"type":"number",
|
||||
"description": "texture scrolling speed",
|
||||
"default": 0.005,
|
||||
"minimum": 0.000001,
|
||||
"maximum": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"three_object":{
|
||||
"title": "Object3D",
|
||||
"description": "Object3D is THREE's abstract base entity class",
|
||||
"url": "https://threejs.org/docs/?q=Object3D#Object3D",
|
||||
"properties":{
|
||||
"_three-type": { "title": "type", "type": "string", "default":"object3D", "description": "needed for blender UI"},
|
||||
"-three-renderOrder":{
|
||||
"type":"integer",
|
||||
"minimum": 0,
|
||||
"maximum": 100
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"janus": {
|
||||
"oneOf":[
|
||||
{"$ref": "#/$defs/janus_image"},
|
||||
{"$ref": "#/$defs/janus_video"},
|
||||
{"$ref": "#/$defs/janus_link"}
|
||||
]
|
||||
},
|
||||
|
||||
"janus_image": {
|
||||
"description": "The <image> tag allows displaying images via url, relative file, or data-uri's",
|
||||
"url": "https://janusvr.com/docs/build/roomtag/index.html#Link",
|
||||
"title": "<image>",
|
||||
"properties": {
|
||||
"-janus-tag": {"type":"string", "default":"image"},
|
||||
"-janus-id": {"type":"object.name"},
|
||||
"-janus-col": {"$ref": "#/$defs/-janus-col"},
|
||||
"-janus-lighting": {"$ref": "#/$defs/-janus-lighting"},
|
||||
"-janus-fwd": {"$ref": "#/$defs/-janus-fwd"},
|
||||
"-janus-scale": {"$ref": "#/$defs/-janus-scale"},
|
||||
"-janus-rotation": {"$ref": "#/$defs/-janus-rotation" },
|
||||
"-janus-locked": {"$ref": "#/$defs/-janus-locked"},
|
||||
"-janus-draw-layer": {"$ref": "#/$defs/-janus-draw_layer"}
|
||||
}
|
||||
},
|
||||
"janus_video": {
|
||||
"title": "<video>",
|
||||
"description": "The <video> tag allows displaying videos",
|
||||
"url": "https://janusvr.com/docs/build/roomtag/index.html#Video",
|
||||
"properties": {
|
||||
"-janus-tag": {"type":"string", "default":"video"},
|
||||
"-janus-id": {"type":"object.name"},
|
||||
"-janus-thumb_id": {"$ref": "#/$defs/-janus-thumb_id"},
|
||||
"-janus-col": {"$ref": "#/$defs/-janus-col"},
|
||||
"-janus-lighting": {"$ref": "#/$defs/-janus-lighting"},
|
||||
"-janus-fwd": {"$ref": "#/$defs/-janus-fwd"},
|
||||
"-janus-scale": {"$ref": "#/$defs/-janus-scale"},
|
||||
"-janus-rotation": {"$ref": "#/$defs/-janus-rotation" },
|
||||
"-janus-locked": {"$ref": "#/$defs/-janus-locked"},
|
||||
"-janus-draw-layer": {"$ref": "#/$defs/-janus-draw_layer"},
|
||||
"-janus-gain": { "type":"float", "minimum":0, "maximum":1 }
|
||||
}
|
||||
},
|
||||
"janus_link": {
|
||||
"title": "<link>",
|
||||
"description": "The <link> tag allows creating portals to other scenes",
|
||||
"url": "https://janusvr.com/docs/build/roomtag/index.html#Link",
|
||||
"properties": {
|
||||
"-janus-tag": {"type":"string", "default":"link"},
|
||||
"-janus-url": {"type":"string", "default": "https://..."},
|
||||
"-janus-title": {"type":"string", "default": "my title"},
|
||||
"-janus-thumb_id": {"$ref": "#/$defs/-janus-thumb_id"},
|
||||
"-janus-draw_glow": {"type": "boolean", "default": True },
|
||||
"-janus-draw_text": {"type": "boolean", "default": True },
|
||||
"-janus-auto_load": {"type": "boolean", "default": False },
|
||||
"-janus-mirror": {"type": "boolean", "default": False },
|
||||
"-janus-col": {"$ref": "#/$defs/-janus-col"},
|
||||
"-janus-lighting": {"$ref": "#/$defs/-janus-lighting"},
|
||||
"-janus-fwd": {"$ref": "#/$defs/-janus-fwd"},
|
||||
"-janus-scale": {"$ref": "#/$defs/-janus-scale"},
|
||||
"-janus-rotation": {"$ref": "#/$defs/-janus-rotation" },
|
||||
"-janus-locked": {"$ref": "#/$defs/-janus-locked"},
|
||||
"-janus-draw-layer": {"$ref": "#/$defs/-janus-draw_layer"}
|
||||
}
|
||||
},
|
||||
"janus_object": {
|
||||
"title": "<object>",
|
||||
"description": "The <object> tag allows displaying 3D files",
|
||||
"url": "https://janusvr.com/docs/build/roomtag/index.html#Object",
|
||||
"properties": {
|
||||
"-janus-tag": {"type":"string", "default":"object"},
|
||||
"-janus-col": {"$ref": "#/$defs/-janus-col"},
|
||||
"-janus-fwd": {"$ref": "#/$defs/-janus-fwd"},
|
||||
"-janus-scale": {"$ref": "#/$defs/-janus-scale"},
|
||||
"-janus-rotation": {"$ref": "#/$defs/-janus-rotation" },
|
||||
"-janus-locked": {"$ref": "#/$defs/-janus-locked"},
|
||||
"-janus-lighting": {"$ref": "#/$defs/-janus-lighting"},
|
||||
"-janus-draw-layer": {"$ref": "#/$defs/-janus-draw_layer"}
|
||||
}
|
||||
},
|
||||
|
||||
"-janus-thumb_id": {"type":"object.name"},
|
||||
"-janus-lighting": {"type": "boolean", "default": False },
|
||||
"-janus-col": {"type": "color", "default": "#FFFFFF"},
|
||||
"-janus-fwd": {"type": "string", "default": "0 0 1"},
|
||||
"-janus-scale": {"type": "string", "default": "1 1 1"},
|
||||
"-janus-rotation": {"type": "string", "default": "0 0 0"},
|
||||
"-janus-locked": {"type": "boolean", "default": False},
|
||||
"-janus-draw_layer": {"type": "integer", "default": 0, "minimum":0, "maximum": 100},
|
||||
|
||||
"godot3": {
|
||||
"oneOf": [
|
||||
{ "$ref": "#/$defs/godot3_material" },
|
||||
{ "$ref": "#/$defs/godot3_spatial" }
|
||||
]
|
||||
},
|
||||
|
||||
"godot3_material": {
|
||||
"title": "SpatialMaterial",
|
||||
"description": "The default Pmaterial for Godot 3.",
|
||||
"url": "https://docs.godotengine.org/en/3.0/tutorials/3d/spatial_material.html",
|
||||
"properties": {
|
||||
"_godot3-type": {
|
||||
"title": "type",
|
||||
"type": "string",
|
||||
"default": "SpatialMaterial"
|
||||
},
|
||||
"-godot3-params_blend_mode": {
|
||||
"type": "string",
|
||||
"description": "mat.params_blend_mode = SpatialMaterial.BLEND_MODE_MIX",
|
||||
"enum": [
|
||||
"SpatialMaterial.BLEND_MODE_MIX",
|
||||
"SpatialMaterial.BLEND_MODE_ADD",
|
||||
"SpatialMaterial.BLEND_MODE_SUB",
|
||||
"SpatialMaterial.BLEND_MODE_MUL"
|
||||
]
|
||||
},
|
||||
"-godot3-params_cull_mode": {
|
||||
"type": "string",
|
||||
"description": "mat.params_cull_mode = SpatialMaterial.CULL_BACK",
|
||||
"enum": [
|
||||
"SpatialMaterial.CULL_BACK",
|
||||
"SpatialMaterial.CULL_FRONT",
|
||||
"SpatialMaterial.CULL_DISABLED"
|
||||
]
|
||||
},
|
||||
"-godot3-uv1_offset.x+": {
|
||||
"type": "number",
|
||||
"description": "mat.uv1_offset.x += speed * delta",
|
||||
"default": 0.005,
|
||||
"minimum": 0.000001,
|
||||
"maximum": 1
|
||||
},
|
||||
"-godot3-uv1_offset.y+": {
|
||||
"type": "number",
|
||||
"description": "mat.uv1_offset.y += speed * delta",
|
||||
"default": 0.005,
|
||||
"minimum": 0.000001,
|
||||
"maximum": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"godot3_spatial": {
|
||||
"title": "Object",
|
||||
"properties": {
|
||||
"_godot3-type": {
|
||||
"title": "type",
|
||||
"type": "string",
|
||||
"default": "Object"
|
||||
},
|
||||
"-godot3-render_priority": {
|
||||
"type": "integer",
|
||||
"description": "mat.render_priority = value",
|
||||
"minimum": -128,
|
||||
"maximum": 127
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"godot4": {
|
||||
"oneOf": [ { "$ref": "#/$defs/godot4_material" }, { "$ref": "#/$defs/godot4_node3d" } ]
|
||||
},
|
||||
"godot4_material": {
|
||||
"title": "StandardMaterial3D",
|
||||
"description": "The default PBR material for Godot 4.",
|
||||
"url": "https://docs.godotengine.org/en/stable/classes/class_standardmaterial3d.html",
|
||||
"properties": {
|
||||
"_godot4-type": {
|
||||
"title": "type",
|
||||
"type": "string",
|
||||
"default": "StandardMaterial3D"
|
||||
},
|
||||
"-gotod4-blend_mode": {
|
||||
"type": "string",
|
||||
"description": "mat.blend_mode = BaseMaterial3D.BLEND_MODE_MIX",
|
||||
"enum": [
|
||||
"BaseMaterial3D.BLEND_MODE_MIX",
|
||||
"BaseMaterial3D.BLEND_MODE_ADD",
|
||||
"BaseMaterial3D.BLEND_MODE_SUB",
|
||||
"BaseMaterial3D.BLEND_MODE_MUL"
|
||||
]
|
||||
},
|
||||
"-gotod4-cull_mode": {
|
||||
"type": "string",
|
||||
"description": "mat.cull_mode = BaseMaterial3D.CULL_BACK",
|
||||
"enum": [
|
||||
"BaseMaterial3D.CULL_BACK",
|
||||
"BaseMaterial3D.CULL_FRONT",
|
||||
"BaseMaterial3D.CULL_DISABLED"
|
||||
]
|
||||
},
|
||||
"-gotod4-uv1_offset.x+": {
|
||||
"type": "number",
|
||||
"description": "mat.uv1_offset.x += speed * delta",
|
||||
"default": 0.005,
|
||||
"minimum": 0.000001,
|
||||
"maximum": 1
|
||||
},
|
||||
"-gotod4-uv1_offset.y+": {
|
||||
"type": "number",
|
||||
"description": "mat.uv1_offset.y += speed * delta",
|
||||
"default": 0.005,
|
||||
"minimum": 0.000001,
|
||||
"maximum": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"godot4_node3d": {
|
||||
"title": "Node3D",
|
||||
"properties": {
|
||||
"_godot4-type": {
|
||||
"title": "type",
|
||||
"type": "string",
|
||||
"default": "Node3D"
|
||||
},
|
||||
"-godot4-render_priority": {
|
||||
"type": "integer",
|
||||
"description": "mat.render_priority = value",
|
||||
"minimum": -128,
|
||||
"maximum": 127
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"playcanvas": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/$defs/playcanvas_material"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/playcanvas_entity"
|
||||
}
|
||||
]
|
||||
},
|
||||
"playcanvas_material": {
|
||||
"title": "StandardMaterial",
|
||||
"properties": {
|
||||
"_playcanvas-type": {
|
||||
"title": "type",
|
||||
"type": "string",
|
||||
"default": "material",
|
||||
"description": "var mat = new pc.StandardMaterial()"
|
||||
},
|
||||
"-playcanvas-material.blendType": {
|
||||
"type": "string",
|
||||
"description": "mat.blendType = pc.BLEND_NORMAL",
|
||||
"enum": [
|
||||
"pc.BLEND_NORMAL",
|
||||
"pc.BLEND_NONE",
|
||||
"pc.BLEND_ADDITIVE",
|
||||
"pc.BLEND_SUBTRACTIVE",
|
||||
"pc.BLEND_MULTIPLICATIVE"
|
||||
]
|
||||
},
|
||||
"-playcanvas-material.cull": {
|
||||
"type": "string",
|
||||
"description": "mat.cull = pc.CULL_BACK",
|
||||
"enum": [
|
||||
"pc.CULL_BACK",
|
||||
"pc.CULL_FRONT",
|
||||
"pc.CULL_NONE"
|
||||
]
|
||||
},
|
||||
"-playcanvas-material.diffuseMapOffset.x": {
|
||||
"type": "number",
|
||||
"description": "mat.diffuseMapOffset.x += speed * dt",
|
||||
"default": 0.005,
|
||||
"minimum": 0.000001,
|
||||
"maximum": 1
|
||||
},
|
||||
"-playcanvas-material.diffuseMapOffset.y": {
|
||||
"type": "number",
|
||||
"description": "mat.diffuseMapOffset.y += speed * dt",
|
||||
"default": 0.005,
|
||||
"minimum": 0.000001,
|
||||
"maximum": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"playcanvas_entity": {
|
||||
"title": "Entity",
|
||||
"properties": {
|
||||
"_playcanvas-type": {
|
||||
"title": "type",
|
||||
"type": "string",
|
||||
"default": "entity"
|
||||
},
|
||||
"-playcanvas-renderOrder": {
|
||||
"type": "integer",
|
||||
"description": "entity.render.layers = [layerID] // Custom sorting via layers or drawOrder",
|
||||
"minimum": 0,
|
||||
"maximum": 100
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"babylon": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/$defs/babylon_material"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/babylon_node"
|
||||
}
|
||||
]
|
||||
},
|
||||
"babylon_material": {
|
||||
"title": "StandardMaterial",
|
||||
"properties": {
|
||||
"_babylon-type": {
|
||||
"title": "type",
|
||||
"type": "string",
|
||||
"default": "StandardMaterial"
|
||||
},
|
||||
"-babylon-material.alphaMode": {
|
||||
"type": "string",
|
||||
"description": "mat.alphaMode = BABYLON.Engine.ALPHA_COMBINE",
|
||||
"enum": [
|
||||
"BABYLON.Engine.ALPHA_COMBINE",
|
||||
"BABYLON.Engine.ALPHA_DISABLE",
|
||||
"BABYLON.Engine.ALPHA_ADD",
|
||||
"BABYLON.Engine.ALPHA_SUBTRACT",
|
||||
"BABYLON.Engine.ALPHA_MULTIPLY"
|
||||
]
|
||||
},
|
||||
"-babylon-material.sideOrientation": {
|
||||
"type": "string",
|
||||
"description": "mat.backFaceCulling = True // or mat.sideOrientation",
|
||||
"enum": [
|
||||
"BABYLON.Material.ClockWiseSideOrientation",
|
||||
"BABYLON.Material.CounterClockWiseSideOrientation",
|
||||
"BABYLON.Material.TwoSidedLighting"
|
||||
]
|
||||
},
|
||||
"-babylon-material.diffuseTexture.uOffset": {
|
||||
"type": "number",
|
||||
"description": "mat.diffuseTexture.uOffset += speed * scene.getAnimationRatio()",
|
||||
"default": 0.005,
|
||||
"minimum": 0.000001,
|
||||
"maximum": 1
|
||||
},
|
||||
"-babylon-material.diffuseTexture.vOffset": {
|
||||
"type": "number",
|
||||
"description": "mat.diffuseTexture.vOffset += speed * scene.getAnimationRatio()",
|
||||
"default": 0.005,
|
||||
"minimum": 0.000001,
|
||||
"maximum": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"babylon_node": {
|
||||
"title": "AbstractMesh",
|
||||
"properties": {
|
||||
"_babylon-type": {
|
||||
"title": "type",
|
||||
"type": "string",
|
||||
"default": "mesh",
|
||||
"description": "var mesh = new BABYLON.Mesh('mesh', scene)"
|
||||
},
|
||||
"-babylon-renderingGroupId": {
|
||||
"type": "integer",
|
||||
"description": "mesh.renderingGroupId = value // 0 to 3 by default",
|
||||
"minimum": 0,
|
||||
"maximum": 100
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
####### JSONSchema-to-form-to-custom-properties generator
|
||||
#
|
||||
# The code below was written based on LLM-generated boilerplate.
|
||||
# I've had to rewrite every function, so at best it was a very convoluted
|
||||
# way to lookup all necessary function-names.
|
||||
#
|
||||
# model: Gemini3 (fast)
|
||||
# prompt: write a jsonschemaform-tab-to-custom properties-tab blender script.
|
||||
# The form should appear in a tab of Object properties, and render a
|
||||
# statically defined jsonschema with: oneOf, anyOf, allOf, $ref & $defs,
|
||||
# datatypes like float,string,int,boolean, nesting, enums, default, title
|
||||
# (for human form-display), enumHuman (for human enum display), description,
|
||||
# pattern, minimum, maximum,
|
||||
# Regarding output: when clicking an apply-button, write the JSON-object to
|
||||
# the custom property-tab as 'JSON' with valuetype 'string'.
|
||||
# If 'flatten' is set on the schema-root, collect all selected key/value
|
||||
# properties from the form, and write each key/value separately to the custom
|
||||
# property tab (only integer, string, boolean, float properties).
|
||||
|
||||
# --- 2. HELPERS ---
|
||||
|
||||
def resolve_schema_node(node, schema_root):
|
||||
if "$ref" in node:
|
||||
ref_path = node["$ref"].split('/')
|
||||
resolved = schema_root
|
||||
for part in ref_path[1:]:
|
||||
resolved = resolved.get(part, {})
|
||||
return resolved
|
||||
return node
|
||||
|
||||
def get_scene_object_items(self, context):
|
||||
items = ["-- select janus asset --"]
|
||||
# Use a set to ensure unique names if necessary,
|
||||
# but obj.name is always unique in Blender
|
||||
for obj in context.scene.objects:
|
||||
items.append((obj.name, obj.name, f"Object: {obj.name}"))
|
||||
|
||||
if not items:
|
||||
return [("NONE", "No Objects Found", "")]
|
||||
|
||||
# Optional: Sort alphabetically
|
||||
items.sort(key=lambda x: x[0])
|
||||
return items
|
||||
|
||||
def tell_if_const(p_name, p_info):
|
||||
return p_name.startswith("_") or (p_info.get("type") == "string" and "default" in p_info and len(p_info) == 2)
|
||||
|
||||
def rgb_to_hex(rgb):
|
||||
"""Converts linear RGB floats to sRGB Hex string."""
|
||||
# Gamma correction: Linear to sRGB
|
||||
srgb = [pow(c, 1/2.2) if c > 0 else 0 for c in rgb]
|
||||
return "#%02x%02x%02x" % tuple(int(max(0, min(255, c * 255))) for c in srgb)
|
||||
|
||||
# --- 3. BAKE OPERATOR ---
|
||||
|
||||
class OBJECT_OT_bake_schema(bpy.types.Operator):
|
||||
bl_idname = "object.bake_schema"
|
||||
bl_label = "Apply"
|
||||
|
||||
def execute(self, context):
|
||||
obj = context.object
|
||||
active_engine = obj.engine
|
||||
|
||||
if active_engine == "NONE":
|
||||
self.report({'WARNING'}, "No engine selected.")
|
||||
return {'CANCELLED'}
|
||||
|
||||
engine_opt = next((opt for opt in SCHEMA["properties"]["engine_settings"]["oneOf"]
|
||||
if opt["engine_val"] == active_engine), None)
|
||||
|
||||
if not engine_opt:
|
||||
return {'FINISHED'}
|
||||
|
||||
baked_data = {}
|
||||
root_node = resolve_schema_node(engine_opt, SCHEMA)
|
||||
self.collect_visible_props(obj, root_node, baked_data)
|
||||
|
||||
if SCHEMA.get("flatten", True):
|
||||
for k, v in baked_data.items():
|
||||
obj[k] = v
|
||||
else:
|
||||
obj["JSON"] = json.dumps(baked_data, indent=2, sort_keys=True)
|
||||
self.report({'INFO'}, f"Baked {active_engine} configuration.")
|
||||
return {'FINISHED'}
|
||||
|
||||
def collect_visible_props(self, obj, node, data):
|
||||
# 1. Handle choice-based routing
|
||||
if "oneOf" in node:
|
||||
options = node["oneOf"]
|
||||
|
||||
if len(options) > 1:
|
||||
# Multiple options: standard discriminator logic
|
||||
first_opt = resolve_schema_node(options[0], SCHEMA)
|
||||
tag_name = list(first_opt.get("properties", {}).keys())[0]
|
||||
|
||||
if hasattr(obj, f"ui_{tag_name}"):
|
||||
current_tag_val = getattr(obj, f"ui_{tag_name}")
|
||||
for option in options:
|
||||
branch = resolve_schema_node(option, SCHEMA)
|
||||
tag_default = branch.get("properties", {}).get(tag_name, {}).get("default")
|
||||
if tag_default == current_tag_val:
|
||||
self.collect_visible_props(obj, branch, data)
|
||||
|
||||
elif len(options) == 1:
|
||||
# Single option: Just dive in
|
||||
branch = resolve_schema_node(options[0], SCHEMA)
|
||||
self.collect_visible_props(obj, branch, data)
|
||||
|
||||
# Note: We return or skip here so we don't process
|
||||
# the same properties twice if they are defined in the branches.
|
||||
if len(options) > 0:
|
||||
# Only return if the oneOf successfully navigated
|
||||
# (Optional: check for shared properties below)
|
||||
pass
|
||||
|
||||
# 2. Collect actual property values in this node
|
||||
properties = node.get("properties", {})
|
||||
for p_name, p_info in properties.items():
|
||||
attr_name = f"ui_{p_name}"
|
||||
is_const = tell_if_const(p_name, p_info)
|
||||
|
||||
if is_const:
|
||||
data[p_name] = p_info["default"]
|
||||
elif hasattr(obj, attr_name):
|
||||
val = getattr(obj, attr_name)
|
||||
|
||||
# Check if this specific property is defined as a color in the schema
|
||||
resolved_info = resolve_schema_node(p_info, SCHEMA)
|
||||
if resolved_info.get("type") == "color":
|
||||
# Convert the FloatVector (RGB) to "#RRGGBB"
|
||||
data[p_name] = rgb_to_hex(val)
|
||||
else:
|
||||
# Handle normal strings, ints, floats, etc.
|
||||
data[p_name] = val
|
||||
|
||||
# --- 4. PANEL ---
|
||||
|
||||
class VIEW3D_PT_json_schema_props(bpy.types.Panel):
|
||||
bl_label = SCHEMA["title"]
|
||||
bl_idname = "VIEW3D_PT_json_schema_props"
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "object"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
obj = context.object
|
||||
layout.prop(obj, "engine")
|
||||
|
||||
active_engine = obj.engine
|
||||
if active_engine == "NONE": return
|
||||
|
||||
engine_opt = next((opt for opt in SCHEMA["properties"]["engine_settings"]["oneOf"]
|
||||
if opt["engine_val"] == active_engine), None)
|
||||
|
||||
if engine_opt:
|
||||
box = layout.box()
|
||||
resolved_node = resolve_schema_node(engine_opt, SCHEMA)
|
||||
self.draw_schema_node(box, obj, resolved_node)
|
||||
|
||||
layout.operator("object.bake_schema")
|
||||
|
||||
def draw_schema_node(self, layout, obj, node):
|
||||
"""Recursively renders the schema UI with support for conditional branching."""
|
||||
|
||||
if "description" in node or "url" in node:
|
||||
# Create a row and explicitly set alignment to LEFT
|
||||
row = layout.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
if "url" in node:
|
||||
op = row.operator("wm.url_open", text="", icon='HELP', emboss=False)
|
||||
op.url = node["url"]
|
||||
if "description" in node:
|
||||
row.label(text=node["description"] )
|
||||
layout.separator()
|
||||
|
||||
# 1. Handle oneOf Routing
|
||||
if "oneOf" in node:
|
||||
options = node["oneOf"]
|
||||
|
||||
if len(options) > 1:
|
||||
# Multiple options: We need a selector (e.g., -janus-tag)
|
||||
first_option = resolve_schema_node(options[0], SCHEMA)
|
||||
tag_name = list(first_option.get("properties", {}).keys())[0]
|
||||
|
||||
# Draw the dropdown selector
|
||||
layout.prop(obj, f"ui_{tag_name}")
|
||||
|
||||
# Match current selection to a branch
|
||||
current_val = getattr(obj, f"ui_{tag_name}")
|
||||
for option in options:
|
||||
branch = resolve_schema_node(option, SCHEMA)
|
||||
tag_default = branch.get("properties", {}).get(tag_name, {}).get("default")
|
||||
|
||||
if tag_default == current_val:
|
||||
# Recurse into the matching branch contents
|
||||
self.draw_schema_node(layout, obj, branch)
|
||||
return # Exit after processing oneOf to prevent double-drawing parent props
|
||||
|
||||
elif len(options) == 1:
|
||||
# Single option wrapper: Dive straight in without drawing a selector
|
||||
branch = resolve_schema_node(options[0], SCHEMA)
|
||||
self.draw_schema_node(layout, obj, branch)
|
||||
# After the branch is drawn, we continue to draw any local properties
|
||||
# (like -janus-lighting) defined at this level.
|
||||
|
||||
# 2. Draw standard properties for the current node
|
||||
properties = node.get("properties", {})
|
||||
for p_name, p_info in properties.items():
|
||||
# Skip 'const' properties (Internal tags that shouldn't be edited)
|
||||
is_const = tell_if_const(p_name, p_info)
|
||||
if is_const:
|
||||
continue
|
||||
|
||||
attr_name = f"ui_{p_name}"
|
||||
if hasattr(obj, attr_name):
|
||||
# Safety check: If this property was already drawn as a 'oneOf' router
|
||||
# in a parent call, we skip it here to avoid duplicates.
|
||||
if "oneOf" in node:
|
||||
first_opt = resolve_schema_node(node["oneOf"][0], SCHEMA)
|
||||
if p_name == list(first_opt.get("properties", {}).keys())[0]:
|
||||
continue
|
||||
|
||||
layout.prop(obj, attr_name)
|
||||
|
||||
# --- 5. REGISTRATION ---
|
||||
def register():
|
||||
# 1. Register top-level engine selector
|
||||
enum_items = [tuple(item) for item in SCHEMA["properties"]["engine"]["enum"]]
|
||||
bpy.types.Object.engine = bpy.props.EnumProperty(
|
||||
items=enum_items,
|
||||
default="NONE"
|
||||
)
|
||||
|
||||
def create_and_set_prop(prop_name, schema_node, override_enum_items=None):
|
||||
"""Unified helper to map JSON schema nodes to Blender properties."""
|
||||
attr_name = f"ui_{prop_name}"
|
||||
if hasattr(bpy.types.Object, attr_name):
|
||||
return
|
||||
|
||||
info = resolve_schema_node(schema_node, SCHEMA)
|
||||
label = prop_name # prop_name.replace("-", " ").strip().title()
|
||||
if info.get("title"):
|
||||
label = info.get("title")
|
||||
prop_type = info.get("type")
|
||||
common = {"description": info.get("description", "")}
|
||||
if info.get("minimum") != None:
|
||||
common["min"] = info.get("minimum")
|
||||
if info.get("maximum") != None:
|
||||
common["max"] = info.get("maximum")
|
||||
|
||||
if override_enum_items:
|
||||
prop = bpy.props.EnumProperty(**common,name=label, items=override_enum_items)
|
||||
elif "enum" in info:
|
||||
items = [(v, v, "") for v in info["enum"]]
|
||||
# Set default if it exists in the schema
|
||||
default_val = info.get("default", items[0][0])
|
||||
prop = bpy.props.EnumProperty(**common,name=label, items=items, default=default_val)
|
||||
elif prop_type == "string":
|
||||
prop = bpy.props.StringProperty(**common,name=label, default=info.get("default", ""))
|
||||
elif prop_type == "integer":
|
||||
prop = bpy.props.IntProperty(**common,name=label, default=info.get("default", 0))
|
||||
elif prop_type == "boolean":
|
||||
prop = bpy.props.BoolProperty(**common,name=label, default=info.get("default", False))
|
||||
elif prop_type == "number":
|
||||
# Blender uses FloatProperty for floating point numbers
|
||||
prop = bpy.props.FloatProperty(
|
||||
**common,
|
||||
name=label,
|
||||
default=info.get("default", 0.0),
|
||||
precision=6 # Optional: controls how many decimals show in UI
|
||||
|
||||
)
|
||||
elif prop_type == "color":
|
||||
# Convert hex default to RGB float tuple for Blender UI
|
||||
def_hex = info.get("default", "#FFFFFF").lstrip('#')
|
||||
def_rgb = tuple(int(def_hex[i:i+2], 16) / 255 for i in (0, 2, 4))
|
||||
|
||||
prop = bpy.props.FloatVectorProperty(
|
||||
**common,
|
||||
name=label,
|
||||
subtype='COLOR',
|
||||
default=def_rgb,
|
||||
size=3,
|
||||
min=0.0, max=1.0
|
||||
)
|
||||
|
||||
# custom types
|
||||
elif prop_type == "object.name":
|
||||
prop = bpy.props.EnumProperty(**common,name=label, items=get_scene_object_items)
|
||||
else:
|
||||
return
|
||||
|
||||
setattr(bpy.types.Object, attr_name, prop)
|
||||
|
||||
# 2. Iterate through definitions
|
||||
for def_name, def_content in SCHEMA["$defs"].items():
|
||||
|
||||
# If the definition IS the property (a leaf node like -janus-thumb_id)
|
||||
if "type" in def_content:
|
||||
create_and_set_prop(def_name, def_content)
|
||||
|
||||
# Handle Object-style definitions (standard properties)
|
||||
props = def_content.get("properties", {})
|
||||
for p_name, p_node in props.items():
|
||||
create_and_set_prop(p_name, p_node)
|
||||
|
||||
# Method A: Handle direct oneOf (The Router/Discriminator)
|
||||
# CHANGE: Only treat as a router if there are MULTIPLE branches.
|
||||
if "oneOf" in def_content and len(def_content["oneOf"]) > 1:
|
||||
first_branch = resolve_schema_node(def_content["oneOf"][0], SCHEMA)
|
||||
# Ensure properties exists before grabbing key
|
||||
branch_props = first_branch.get("properties", {})
|
||||
if branch_props:
|
||||
tag_name = list(branch_props.keys())[0]
|
||||
|
||||
branch_items = []
|
||||
for opt in def_content["oneOf"]:
|
||||
res = resolve_schema_node(opt, SCHEMA)
|
||||
# Use .get() to avoid KeyError if 'default' is missing
|
||||
val = res.get("properties", {}).get(tag_name, {}).get("default", "")
|
||||
name = res.get("title", val.title() if val else "Option")
|
||||
branch_items.append((val, name, ""))
|
||||
|
||||
create_and_set_prop(tag_name, branch_props[tag_name], override_enum_items=branch_items)
|
||||
|
||||
# Method B: Handle standard properties
|
||||
props = def_content.get("properties", {})
|
||||
for p_name, p_node in props.items():
|
||||
create_and_set_prop(p_name, p_node)
|
||||
|
||||
# Finalize registration
|
||||
bpy.utils.register_class(OBJECT_OT_bake_schema)
|
||||
bpy.utils.register_class(VIEW3D_PT_json_schema_props)
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
||||
469
schema/xrfragment-engine-prefixes.json
Normal file
469
schema/xrfragment-engine-prefixes.json
Normal file
|
|
@ -0,0 +1,469 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://xrfragment.org/schema/xrfragment-engine-prefixes.json",
|
||||
"$version": 34,
|
||||
"title": "XR Fragments Engine Prefixes",
|
||||
"type": "object",
|
||||
"flatten": true,
|
||||
|
||||
"properties": {
|
||||
"engine": {
|
||||
"type": "enum",
|
||||
"enum": [
|
||||
["NONE", "-- Select --", ""],
|
||||
["JANUSXR", "JanusXR", ""],
|
||||
["THREEJS", "Three.js", ""],
|
||||
["GODOT3", "Godot 3", ""],
|
||||
["GODOT4", "Godot 4", ""],
|
||||
["AFRAME", "AFRAME", ""],
|
||||
["BABYLON", "Babylon.js", ""],
|
||||
["PLAYCANVAS", "PlayCanvas", ""]
|
||||
],
|
||||
"default": "NONE"
|
||||
},
|
||||
"engine_settings": {
|
||||
"oneOf": [
|
||||
{"engine_val": "JANUSXR", "$ref": "#/$defs/janus", "title": "JanusXR"},
|
||||
{"engine_val": "THREEJS", "$ref": "#/$defs/three", "title": "THREE.js"},
|
||||
{"engine_val": "GODOT3", "$ref": "#/$defs/godot3", "title": "Godot3 (beta)"},
|
||||
{"engine_val": "GODOT4", "$ref": "#/$defs/godot4", "title": "Godot4 (beta)"},
|
||||
{"engine_val": "AFRAME", "$ref": "#/$defs/aframe", "title": "AFRAME (beta)"},
|
||||
{"engine_val": "BABYLON", "$ref": "#/$defs/babylon", "title": "Babylon.js (beta)"},
|
||||
{"engine_val": "PLAYCANVAS", "$ref": "#/$defs/playcanvas", "title": "PlayCanvas (beta)"}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
"$defs": {
|
||||
|
||||
|
||||
"three": {
|
||||
"oneOf":[
|
||||
{"$ref": "#/$defs/three_material"},
|
||||
{"$ref": "#/$defs/three_object"}
|
||||
]
|
||||
},
|
||||
|
||||
"aframe": {
|
||||
"oneOf":[
|
||||
{"$ref": "#/$defs/three_material"},
|
||||
{"$ref": "#/$defs/three_object"}
|
||||
]
|
||||
},
|
||||
|
||||
"three_material":{
|
||||
"title": "Material",
|
||||
"description": "Material is THREE's abstract base material class",
|
||||
"url": "https://threejs.org/docs/?q=material#Material",
|
||||
"properties":{
|
||||
"_three-type": { "title": "type", "type": "string", "default":"material", "description": "needed for blender UI"},
|
||||
"-three-material.blending":{
|
||||
"type":"string",
|
||||
"enum": ["THREE.NormalBlending", "THREE.NoBlending", "THREE.AdditiveBlending", "THREE.SubtractiveBlending", "THREE.MultiplyBlending" ]
|
||||
},
|
||||
"-three-material.sides":{
|
||||
"type":"string",
|
||||
"description": "culling: which sides to render (less increases fps)",
|
||||
"enum": ["THREE.FrontSide","THREE.BackSide","THREE.DoubleSide"]
|
||||
},
|
||||
"-three-material.map.offset.x+":{
|
||||
"type":"number",
|
||||
"description": "texture scrolling speed",
|
||||
"default": 0.005,
|
||||
"minimum": 0.000001,
|
||||
"maximum": 1
|
||||
},
|
||||
"-three-material.map.offset.yx+":{
|
||||
"type":"number",
|
||||
"description": "texture scrolling speed",
|
||||
"default": 0.005,
|
||||
"minimum": 0.000001,
|
||||
"maximum": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"three_object":{
|
||||
"title": "Object3D",
|
||||
"description": "Object3D is THREE's abstract base entity class",
|
||||
"url": "https://threejs.org/docs/?q=Object3D#Object3D",
|
||||
"properties":{
|
||||
"_three-type": { "title": "type", "type": "string", "default":"object3D", "description": "needed for blender UI"},
|
||||
"-three-renderOrder":{
|
||||
"type":"integer",
|
||||
"minimum": 0,
|
||||
"maximum": 100
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"janus": {
|
||||
"oneOf":[
|
||||
{"$ref": "#/$defs/janus_image"},
|
||||
{"$ref": "#/$defs/janus_video"},
|
||||
{"$ref": "#/$defs/janus_link"}
|
||||
]
|
||||
},
|
||||
|
||||
"janus_image": {
|
||||
"description": "The <image> tag allows displaying images via url, relative file, or data-uri's",
|
||||
"url": "https://janusvr.com/docs/build/roomtag/index.html#Link",
|
||||
"title": "<image>",
|
||||
"properties": {
|
||||
"-janus-tag": {"type":"string", "default":"image"},
|
||||
"-janus-id": {"type":"object.name"},
|
||||
"-janus-col": {"$ref": "#/$defs/-janus-col"},
|
||||
"-janus-lighting": {"$ref": "#/$defs/-janus-lighting"},
|
||||
"-janus-fwd": {"$ref": "#/$defs/-janus-fwd"},
|
||||
"-janus-scale": {"$ref": "#/$defs/-janus-scale"},
|
||||
"-janus-rotation": {"$ref": "#/$defs/-janus-rotation" },
|
||||
"-janus-locked": {"$ref": "#/$defs/-janus-locked"},
|
||||
"-janus-draw-layer": {"$ref": "#/$defs/-janus-draw_layer"}
|
||||
}
|
||||
},
|
||||
"janus_video": {
|
||||
"title": "<video>",
|
||||
"description": "The <video> tag allows displaying videos",
|
||||
"url": "https://janusvr.com/docs/build/roomtag/index.html#Video",
|
||||
"properties": {
|
||||
"-janus-tag": {"type":"string", "default":"video"},
|
||||
"-janus-id": {"type":"object.name"},
|
||||
"-janus-thumb_id": {"$ref": "#/$defs/-janus-thumb_id"},
|
||||
"-janus-col": {"$ref": "#/$defs/-janus-col"},
|
||||
"-janus-lighting": {"$ref": "#/$defs/-janus-lighting"},
|
||||
"-janus-fwd": {"$ref": "#/$defs/-janus-fwd"},
|
||||
"-janus-scale": {"$ref": "#/$defs/-janus-scale"},
|
||||
"-janus-rotation": {"$ref": "#/$defs/-janus-rotation" },
|
||||
"-janus-locked": {"$ref": "#/$defs/-janus-locked"},
|
||||
"-janus-draw-layer": {"$ref": "#/$defs/-janus-draw_layer"},
|
||||
"-janus-gain": { "type":"float", "minimum":0, "maximum":1 }
|
||||
}
|
||||
},
|
||||
"janus_link": {
|
||||
"title": "<link>",
|
||||
"description": "The <link> tag allows creating portals to other scenes",
|
||||
"url": "https://janusvr.com/docs/build/roomtag/index.html#Link",
|
||||
"properties": {
|
||||
"-janus-tag": {"type":"string", "default":"link"},
|
||||
"-janus-url": {"type":"string", "default": "https://..."},
|
||||
"-janus-title": {"type":"string", "default": "my title"},
|
||||
"-janus-thumb_id": {"$ref": "#/$defs/-janus-thumb_id"},
|
||||
"-janus-draw_glow": {"type": "boolean", "default": true },
|
||||
"-janus-draw_text": {"type": "boolean", "default": true },
|
||||
"-janus-auto_load": {"type": "boolean", "default": false },
|
||||
"-janus-mirror": {"type": "boolean", "default": false },
|
||||
"-janus-col": {"$ref": "#/$defs/-janus-col"},
|
||||
"-janus-lighting": {"$ref": "#/$defs/-janus-lighting"},
|
||||
"-janus-fwd": {"$ref": "#/$defs/-janus-fwd"},
|
||||
"-janus-scale": {"$ref": "#/$defs/-janus-scale"},
|
||||
"-janus-rotation": {"$ref": "#/$defs/-janus-rotation" },
|
||||
"-janus-locked": {"$ref": "#/$defs/-janus-locked"},
|
||||
"-janus-draw-layer": {"$ref": "#/$defs/-janus-draw_layer"}
|
||||
}
|
||||
},
|
||||
"janus_object": {
|
||||
"title": "<object>",
|
||||
"description": "The <object> tag allows displaying 3D files",
|
||||
"url": "https://janusvr.com/docs/build/roomtag/index.html#Object",
|
||||
"properties": {
|
||||
"-janus-tag": {"type":"string", "default":"object"},
|
||||
"-janus-col": {"$ref": "#/$defs/-janus-col"},
|
||||
"-janus-fwd": {"$ref": "#/$defs/-janus-fwd"},
|
||||
"-janus-scale": {"$ref": "#/$defs/-janus-scale"},
|
||||
"-janus-rotation": {"$ref": "#/$defs/-janus-rotation" },
|
||||
"-janus-locked": {"$ref": "#/$defs/-janus-locked"},
|
||||
"-janus-lighting": {"$ref": "#/$defs/-janus-lighting"},
|
||||
"-janus-draw-layer": {"$ref": "#/$defs/-janus-draw_layer"}
|
||||
}
|
||||
},
|
||||
|
||||
"-janus-thumb_id": {"type":"object.name"},
|
||||
"-janus-lighting": {"type": "boolean", "default": false },
|
||||
"-janus-col": {"type": "color", "default": "#FFFFFF"},
|
||||
"-janus-fwd": {"type": "string", "default": "0 0 1"},
|
||||
"-janus-scale": {"type": "string", "default": "1 1 1"},
|
||||
"-janus-rotation": {"type": "string", "default": "0 0 0"},
|
||||
"-janus-locked": {"type": "boolean", "default": false},
|
||||
"-janus-draw_layer": {"type": "integer", "default": 0, "minimum":0, "maximum": 100},
|
||||
|
||||
"godot3": {
|
||||
"oneOf": [
|
||||
{ "$ref": "#/$defs/godot3_material" },
|
||||
{ "$ref": "#/$defs/godot3_spatial" }
|
||||
]
|
||||
},
|
||||
|
||||
"godot3_material": {
|
||||
"title": "SpatialMaterial",
|
||||
"description": "The default Pmaterial for Godot 3.",
|
||||
"url": "https://docs.godotengine.org/en/3.0/tutorials/3d/spatial_material.html",
|
||||
"properties": {
|
||||
"_godot3-type": {
|
||||
"title": "type",
|
||||
"type": "string",
|
||||
"default": "SpatialMaterial"
|
||||
},
|
||||
"-godot3-params_blend_mode": {
|
||||
"type": "string",
|
||||
"description": "mat.params_blend_mode = SpatialMaterial.BLEND_MODE_MIX",
|
||||
"enum": [
|
||||
"SpatialMaterial.BLEND_MODE_MIX",
|
||||
"SpatialMaterial.BLEND_MODE_ADD",
|
||||
"SpatialMaterial.BLEND_MODE_SUB",
|
||||
"SpatialMaterial.BLEND_MODE_MUL"
|
||||
]
|
||||
},
|
||||
"-godot3-params_cull_mode": {
|
||||
"type": "string",
|
||||
"description": "mat.params_cull_mode = SpatialMaterial.CULL_BACK",
|
||||
"enum": [
|
||||
"SpatialMaterial.CULL_BACK",
|
||||
"SpatialMaterial.CULL_FRONT",
|
||||
"SpatialMaterial.CULL_DISABLED"
|
||||
]
|
||||
},
|
||||
"-godot3-uv1_offset.x+": {
|
||||
"type": "number",
|
||||
"description": "mat.uv1_offset.x += speed * delta",
|
||||
"default": 0.005,
|
||||
"minimum": 0.000001,
|
||||
"maximum": 1
|
||||
},
|
||||
"-godot3-uv1_offset.y+": {
|
||||
"type": "number",
|
||||
"description": "mat.uv1_offset.y += speed * delta",
|
||||
"default": 0.005,
|
||||
"minimum": 0.000001,
|
||||
"maximum": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"godot3_spatial": {
|
||||
"title": "Object",
|
||||
"properties": {
|
||||
"_godot3-type": {
|
||||
"title": "type",
|
||||
"type": "string",
|
||||
"default": "Object"
|
||||
},
|
||||
"-godot3-render_priority": {
|
||||
"type": "integer",
|
||||
"description": "mat.render_priority = value",
|
||||
"minimum": -128,
|
||||
"maximum": 127
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"godot4": {
|
||||
"oneOf": [ { "$ref": "#/$defs/godot4_material" }, { "$ref": "#/$defs/godot4_node3d" } ]
|
||||
},
|
||||
"godot4_material": {
|
||||
"title": "StandardMaterial3D",
|
||||
"description": "The default PBR material for Godot 4.",
|
||||
"url": "https://docs.godotengine.org/en/stable/classes/class_standardmaterial3d.html",
|
||||
"properties": {
|
||||
"_godot4-type": {
|
||||
"title": "type",
|
||||
"type": "string",
|
||||
"default": "StandardMaterial3D"
|
||||
},
|
||||
"-gotod4-blend_mode": {
|
||||
"type": "string",
|
||||
"description": "mat.blend_mode = BaseMaterial3D.BLEND_MODE_MIX",
|
||||
"enum": [
|
||||
"BaseMaterial3D.BLEND_MODE_MIX",
|
||||
"BaseMaterial3D.BLEND_MODE_ADD",
|
||||
"BaseMaterial3D.BLEND_MODE_SUB",
|
||||
"BaseMaterial3D.BLEND_MODE_MUL"
|
||||
]
|
||||
},
|
||||
"-gotod4-cull_mode": {
|
||||
"type": "string",
|
||||
"description": "mat.cull_mode = BaseMaterial3D.CULL_BACK",
|
||||
"enum": [
|
||||
"BaseMaterial3D.CULL_BACK",
|
||||
"BaseMaterial3D.CULL_FRONT",
|
||||
"BaseMaterial3D.CULL_DISABLED"
|
||||
]
|
||||
},
|
||||
"-gotod4-uv1_offset.x+": {
|
||||
"type": "number",
|
||||
"description": "mat.uv1_offset.x += speed * delta",
|
||||
"default": 0.005,
|
||||
"minimum": 0.000001,
|
||||
"maximum": 1
|
||||
},
|
||||
"-gotod4-uv1_offset.y+": {
|
||||
"type": "number",
|
||||
"description": "mat.uv1_offset.y += speed * delta",
|
||||
"default": 0.005,
|
||||
"minimum": 0.000001,
|
||||
"maximum": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"godot4_node3d": {
|
||||
"title": "Node3D",
|
||||
"properties": {
|
||||
"_godot4-type": {
|
||||
"title": "type",
|
||||
"type": "string",
|
||||
"default": "Node3D"
|
||||
},
|
||||
"-godot4-render_priority": {
|
||||
"type": "integer",
|
||||
"description": "mat.render_priority = value",
|
||||
"minimum": -128,
|
||||
"maximum": 127
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"playcanvas": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/$defs/playcanvas_material"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/playcanvas_entity"
|
||||
}
|
||||
]
|
||||
},
|
||||
"playcanvas_material": {
|
||||
"title": "StandardMaterial",
|
||||
"properties": {
|
||||
"_playcanvas-type": {
|
||||
"title": "type",
|
||||
"type": "string",
|
||||
"default": "material",
|
||||
"description": "var mat = new pc.StandardMaterial()"
|
||||
},
|
||||
"-playcanvas-material.blendType": {
|
||||
"type": "string",
|
||||
"description": "mat.blendType = pc.BLEND_NORMAL",
|
||||
"enum": [
|
||||
"pc.BLEND_NORMAL",
|
||||
"pc.BLEND_NONE",
|
||||
"pc.BLEND_ADDITIVE",
|
||||
"pc.BLEND_SUBTRACTIVE",
|
||||
"pc.BLEND_MULTIPLICATIVE"
|
||||
]
|
||||
},
|
||||
"-playcanvas-material.cull": {
|
||||
"type": "string",
|
||||
"description": "mat.cull = pc.CULL_BACK",
|
||||
"enum": [
|
||||
"pc.CULL_BACK",
|
||||
"pc.CULL_FRONT",
|
||||
"pc.CULL_NONE"
|
||||
]
|
||||
},
|
||||
"-playcanvas-material.diffuseMapOffset.x": {
|
||||
"type": "number",
|
||||
"description": "mat.diffuseMapOffset.x += speed * dt",
|
||||
"default": 0.005,
|
||||
"minimum": 0.000001,
|
||||
"maximum": 1
|
||||
},
|
||||
"-playcanvas-material.diffuseMapOffset.y": {
|
||||
"type": "number",
|
||||
"description": "mat.diffuseMapOffset.y += speed * dt",
|
||||
"default": 0.005,
|
||||
"minimum": 0.000001,
|
||||
"maximum": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"playcanvas_entity": {
|
||||
"title": "Entity",
|
||||
"properties": {
|
||||
"_playcanvas-type": {
|
||||
"title": "type",
|
||||
"type": "string",
|
||||
"default": "entity"
|
||||
},
|
||||
"-playcanvas-renderOrder": {
|
||||
"type": "integer",
|
||||
"description": "entity.render.layers = [layerID] // Custom sorting via layers or drawOrder",
|
||||
"minimum": 0,
|
||||
"maximum": 100
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"babylon": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/$defs/babylon_material"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/babylon_node"
|
||||
}
|
||||
]
|
||||
},
|
||||
"babylon_material": {
|
||||
"title": "StandardMaterial",
|
||||
"properties": {
|
||||
"_babylon-type": {
|
||||
"title": "type",
|
||||
"type": "string",
|
||||
"default": "StandardMaterial"
|
||||
},
|
||||
"-babylon-material.alphaMode": {
|
||||
"type": "string",
|
||||
"description": "mat.alphaMode = BABYLON.Engine.ALPHA_COMBINE",
|
||||
"enum": [
|
||||
"BABYLON.Engine.ALPHA_COMBINE",
|
||||
"BABYLON.Engine.ALPHA_DISABLE",
|
||||
"BABYLON.Engine.ALPHA_ADD",
|
||||
"BABYLON.Engine.ALPHA_SUBTRACT",
|
||||
"BABYLON.Engine.ALPHA_MULTIPLY"
|
||||
]
|
||||
},
|
||||
"-babylon-material.sideOrientation": {
|
||||
"type": "string",
|
||||
"description": "mat.backFaceCulling = true // or mat.sideOrientation",
|
||||
"enum": [
|
||||
"BABYLON.Material.ClockWiseSideOrientation",
|
||||
"BABYLON.Material.CounterClockWiseSideOrientation",
|
||||
"BABYLON.Material.TwoSidedLighting"
|
||||
]
|
||||
},
|
||||
"-babylon-material.diffuseTexture.uOffset": {
|
||||
"type": "number",
|
||||
"description": "mat.diffuseTexture.uOffset += speed * scene.getAnimationRatio()",
|
||||
"default": 0.005,
|
||||
"minimum": 0.000001,
|
||||
"maximum": 1
|
||||
},
|
||||
"-babylon-material.diffuseTexture.vOffset": {
|
||||
"type": "number",
|
||||
"description": "mat.diffuseTexture.vOffset += speed * scene.getAnimationRatio()",
|
||||
"default": 0.005,
|
||||
"minimum": 0.000001,
|
||||
"maximum": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"babylon_node": {
|
||||
"title": "AbstractMesh",
|
||||
"properties": {
|
||||
"_babylon-type": {
|
||||
"title": "type",
|
||||
"type": "string",
|
||||
"default": "mesh",
|
||||
"description": "var mesh = new BABYLON.Mesh('mesh', scene)"
|
||||
},
|
||||
"-babylon-renderingGroupId": {
|
||||
"type": "integer",
|
||||
"description": "mesh.renderingGroupId = value // 0 to 3 by default",
|
||||
"minimum": 0,
|
||||
"maximum": 100
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue