diff --git a/.antora/modules/specification/nav-openmaterial.adoc b/.antora/modules/specification/nav-openmaterial.adoc index 4ee25ecf..5684be20 100644 --- a/.antora/modules/specification/nav-openmaterial.adoc +++ b/.antora/modules/specification/nav-openmaterial.adoc @@ -18,11 +18,11 @@ ** xref:geometry/general.adoc[] ** xref:geometry/object-classes.adoc[] ** xref:geometry/file-format-support.adoc[] -** xref:geometry/metadata.adoc[] -** xref:geometry/performance.adoc[] +** xref:geometry/asset-schema.adoc[] +** xref:geometry/mapping-schema.adoc[] * Material ** xref:material/introduction.adoc[] ** xref:material/file-format.adoc[] ** xref:material/metadata.adoc[] -** xref:material/material-properties.adoc[Material properties] -** xref:material/lookup-tables.adoc[] +** xref:material/material-schema.adoc[] +** xref:material/material-emp-schema.adoc[] diff --git a/.github/workflows/antora-build.yml b/.github/workflows/antora-build.yml index 18ba7fd1..d36a9b76 100644 --- a/.github/workflows/antora-build.yml +++ b/.github/workflows/antora-build.yml @@ -45,14 +45,14 @@ jobs: - name: Generate AsciiDoc from JSON working-directory: repo/scripts run: | - python3 json2asciidoc.py ../schemas/asset_schema.json metadata - mv metadata.adoc ../content/geometry/ - python3 json2asciidoc.py ../schemas/material_schema.json metadata - mv metadata.adoc ../content/material/ - python3 json2asciidoc.py ../schemas/material_schema.json materialProperties - mv materialProperties.adoc ../content/material/material-properties.adoc - python3 json2asciidoc.py ../schemas/material_emp_schema.json electromagneticProperties - mv electromagneticProperties.adoc ../content/material/electromagnetic-properties.adoc + python3 json2asciidoc.py ../schemas/asset_schema.json + mv asset-schema.adoc ../content/geometry/ + python3 json2asciidoc.py ../schemas/mapping_schema.json + mv mapping-schema.adoc ../content/geometry/ + python3 json2asciidoc.py ../schemas/material_schema.json + mv material-schema.adoc ../content/material/ + python3 json2asciidoc.py ../schemas/material_emp_schema.json + mv material-emp-schema.adoc ../content/material/ - name: Configure Pages uses: actions/configure-pages@v4 diff --git a/.github/workflows/validate-json.yaml b/.github/workflows/validate-json.yaml index 2c96fb26..32e8b8ee 100644 --- a/.github/workflows/validate-json.yaml +++ b/.github/workflows/validate-json.yaml @@ -24,6 +24,12 @@ jobs: schema: ./schemas/asset_schema.json file: ./examples/*.xoma + - name: Validate material mapping JSON schemas + uses: cardinalby/schema-validator-action@v3 + with: + schema: ./schemas/mapping_schema.json + file: ./examples/*.xomm + - name: Validate material JSON schemas uses: cardinalby/schema-validator-action@v3 with: diff --git a/content/geometry/geometry-index.adoc b/content/geometry/geometry-index.adoc index 1d8915a9..c2eb7e0d 100644 --- a/content/geometry/geometry-index.adoc +++ b/content/geometry/geometry-index.adoc @@ -4,6 +4,6 @@ * xref:geometry/general.adoc[leveloffset=+1] * xref:geometry/object-classes.adoc[leveloffset=+1] * xref:geometry/file-format-support.adoc[leveloffset=+1] -* xref:geometry/metadata.adoc[leveloffset=+1] -* xref:geometry/performance.adoc[leveloffset=+1] +* xref:geometry/asset-schema.adoc[leveloffset=+1] +* xref:geometry/mapping-schema.adoc[leveloffset=+1] diff --git a/content/material/material-index.adoc b/content/material/material-index.adoc index ff3803e3..13f5ebcd 100644 --- a/content/material/material-index.adoc +++ b/content/material/material-index.adoc @@ -3,5 +3,5 @@ * xref:material/introduction.adoc[leveloffset=+1] * xref:material/file-format.adoc[leveloffset=+1] * xref:material/metadata.adoc[leveloffset=+1] -* xref:material/material-properties.adoc[leveloffset=+1] -* xref:material/lookup-tables.adoc[leveloffset=+1] +* xref:material/material-schema.adoc[leveloffset=+1] +* xref:material/material-emp-schema.adoc[leveloffset=+1] diff --git a/examples/example_asset.xoma b/examples/example_asset.xoma index d8105400..a129fbb9 100644 --- a/examples/example_asset.xoma +++ b/examples/example_asset.xoma @@ -26,7 +26,7 @@ "z" : [0.0, 1.5] } }, - "materialMapping": [ - ["10;50;255;127", "example_material.xomp", "metal with red paint"] + "materialTextureAssignment": [ + ["example_material_name", "example_texture.png"] ] } diff --git a/examples/example_mapping.xomm b/examples/example_mapping.xomm new file mode 100644 index 00000000..deb46c11 --- /dev/null +++ b/examples/example_mapping.xomm @@ -0,0 +1,19 @@ +{ + "metadata": { + "name": "example_mapping", + "description": "This is an example for a material mapping table. This file can be shared between multiple assets.", + "uuid": "c492bda27c8443e6be1903115545e844", + "mappingVersion": "1.0.0", + "openMaterialVersion": "1.0.0", + "copyright": "(C) 2023-2024, Example Company", + "license": "MPL-2.0", + "author": "john.doe@asam.net", + "creationDate": "20240703T101728Z", + }, + "materialMapping": [ + ["material_red", "materials/material_a.xomp", "metal with red paint"], + ["rgba:10;50;255;127", "materials/material_a.xomp", "metal with red paint"], + ["material_green", "materials/material_b.xomp", "metal with green paint"], + ["material_blue", "materials/material_c.xomp", "metal with blue paint"] + ] +} diff --git a/schemas/asset_schema.json b/schemas/asset_schema.json index 06c8b81d..498d05a5 100644 --- a/schemas/asset_schema.json +++ b/schemas/asset_schema.json @@ -157,34 +157,28 @@ "boundingBox" ] }, - "materialMapping": { + "materialTextureAssignment": { "type": "array", - "description": "Mapping of materials used in the asset", + "description": "Optional array containing material texture assignments. It links material names contained in the 3D model file to OpenMATERIAL assignment textures. In a separate material mapping file, the 'color' values in this texture are linked to OpenMATERIAL property files.", "items": { "type": "array", - "description": "Array of RGB values and material name", "items": [ { "type": "string", - "description": "RGB values in the format 'R;G;B;A'", - "pattern": "^\\d{1,3};\\d{1,3};\\d{1,3};\\d{1,3}$" + "description": "Name of the material in the 3D model file." }, { "type": "string", - "description": "Path to an OpenMATERIAL property file" - }, - { - "type": "string", - "description": "Short but precise description of the material" + "description": "File path to the OpenMATERIAL assignment texture. Supported texture formats are png and jpg.", + "pattern": "^(\\./|/)?([a-zA-Z0-9_\\-./]+)\\.(png|jpg|jpeg)$" } ], - "minItems": 3, - "maxItems": 3 + "minItems": 2, + "maxItems": 2 } } }, "required": [ - "metadata", - "materialMapping" + "metadata" ] } diff --git a/schemas/mapping_schema.json b/schemas/mapping_schema.json new file mode 100644 index 00000000..5190003f --- /dev/null +++ b/schemas/mapping_schema.json @@ -0,0 +1,78 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Material Mapping Schema", + "type": "object", + "properties": { + "metadata": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Name of the material mapping." + }, + "description": { + "type": "string", + "description": "This is a short description of the material mapping table. Use 2-3 sentences." + }, + "uuid": { + "type": "string", + "description": "Universally unique identifier for the material mapping.", + "pattern": "^[a-f0-9]{32}$" + }, + "mappingVersion": { + "type": "string", + "description": "Version of the mapping table.", + "pattern": "^\\d+\\.\\d+\\.\\d+$" + }, + "openMaterialVersion": { + "type": "string", + "description": "Version of the OpenMaterial format.", + "pattern": "^\\d+\\.\\d+\\.\\d+$" + }, + "copyright": { + "type": "string", + "description": "Copyright information with year and company." + }, + "license": { + "type": "string", + "description": "License information. For common open source licenses, provide an SPDX identifier or a URL to the license." + }, + "author": { + "type": "string", + "description": "Email address or name of the author. Can also be a company name." + }, + "creationDate": { + "type": "string", + "description": "Date and time when the material mapping was created in ISO 8601 format.", + "pattern": "^\\d{8}T\\d{6}Z$" + } + }, + "required": ["name", "uuid", "mappingVersion", "openMaterialVersion", "creationDate"] + }, + "materialMapping": { + "type": "array", + "description": "Array containing material mappings.", + "items": { + "type": "array", + "items": [ + { + "type": "string", + "description": "Material name or RGBA code." + }, + { + "type": "string", + "description": "File path to the material.", + "pattern": "^(\\./|/)?([a-zA-Z0-9_\\-./]+)\\.xomp$" + }, + { + "type": "string", + "description": "Description of the material." + } + ], + "minItems": 3, + "maxItems": 3 + } + } + }, + "required": ["metadata", "materialMapping"] +} diff --git a/scripts/asset-schema.adoc b/scripts/asset-schema.adoc new file mode 100644 index 00000000..1f75fc71 --- /dev/null +++ b/scripts/asset-schema.adoc @@ -0,0 +1,152 @@ += Asset schema + +== metadata + +Metadata about the asset. + +=== name +Name of the asset. + +*Required:* Yes + +=== description +Short description of the asset in 2 - 3 sentences. + +*Required:* Yes + +=== uuid +Universally unique identifier for the asset. + +*Pattern:* `^[a-f0-9]\{32\}$` + +*Required:* Yes + +=== assetVersion +Version of the asset. + +*Pattern:* `^\\d+\\.\\d+\\.\\d+$` + +*Required:* Yes + +=== openMaterialVersion +Version of OpenMATERIAL. + +*Pattern:* `^\\d+\\.\\d+\\.\\d+$` + +*Required:* Yes + +=== copyright +Copyright information with year and company. + +*Required:* Yes + +=== license +License information. For common open source licenses, provide an SPDX identifier. For other types of licenses, provide an URL to a webpage with the license or the filename of a separately provided license file. + +*Required:* Yes + +=== author +Name or email address of the author of this asset. In case of multiple authors, use comma-separation. The author can also be a company name. + +*Required:* Yes + +=== modelCreationTool +The tool or software used to generate the 3D model of this asset. + +*Required:* No + +=== creationDate +Creation date of the asset in the format YYYYMMDDTHHMMSSZ. + +*Pattern:* `^\\d\{8\}T\\d\{6\}Z$` + +*Required:* Yes + +=== modelingMethod +Description how the 3D model was modeled. Was it modeled based on a 3D scan, based on photos, or freehand? + +*Required:* No + +=== validationDescription +Was the geometry of the 3D model validated against a real-world object? If yes, how? + +*Required:* No + +=== assetType +Type of the asset out of the following: object (individual object), scene (a collection of multiple objects) + +*Required:* Yes + +=== objectClass +The class of the object. This only applies if 'asset_type' == 'object'. + +*Required:* Yes + +=== animated +Does the 3D model contain keyframe animations? + +*Required:* Yes + +=== pbrMaterialWorkflow +Does the 3D model contain materials for a PBR workflow? If yes, for a metallic or specular workflow? + +*Required:* Yes + +=== triangleCount +The number of triangles in the 3D model. One quad counts as two triangles. + +*Required:* Yes + +=== meshCount +The number of meshes in the 3D geometry. + +*Required:* Yes + +=== textureResolutions +List of all texture resolutions in the 3D model. This can include any combination of '1K', '2K', or '4K'. + +No description + +*Required:* Yes + +=== normalMapFormat +Does the 3D model have normal maps as textures? If yes, are they using the DirectX format or the OpenGL format? + +*Required:* Yes + +=== boundingBox +Axis-aligned bounding box dimensions specifying the minimum and maximum values in x, y, and z direction. + +*Required:* Yes + +==== x +Minimum and maximum values in m in x direction. + +No description + +*Required:* Yes + +==== y +Minimum and maximum values in m in y direction. + +No description + +*Required:* Yes + +==== z +Minimum and maximum values in m in z direction. + +No description + +*Required:* Yes + +== materialTextureAssignment + +Optional array containing material texture assignments. It links material names contained in the 3D model file to OpenMATERIAL assignment textures. In a separate material mapping file, the 'color' values in this texture are linked to OpenMATERIAL property files. + + +Columns of the table: + +- Column 1: Name of the material in the 3D model file. +- Column 2: File path to the OpenMATERIAL assignment texture. Supported texture formats are png and jpg. + diff --git a/scripts/json2asciidoc.py b/scripts/json2asciidoc.py index 5f642a5a..eefaf47e 100644 --- a/scripts/json2asciidoc.py +++ b/scripts/json2asciidoc.py @@ -1,7 +1,24 @@ +import os import json import argparse +def format_main_headline(file_name): + """ + Convert file name to a title-style headline for the AsciiDoc. + Replaces underscores with spaces and capitalizes the first letter. + + Args: + file_name (str): The base name of the JSON schema file. + + Returns: + str: The formatted headline. + """ + file_name = file_name.replace('_', ' ') # Replace underscores with spaces + file_name = file_name.replace('-', ' ') # Replace hyphens with spaces + return file_name.capitalize() # Capitalize the first letter + + def escape_special_chars(pattern): """ Escape special characters in the pattern string for AsciiDoc compatibility. @@ -107,13 +124,13 @@ def generate_asciidoc(field_name, schema, required_fields): Returns: str: The generated AsciiDoc content. """ - asciidoc_content = f"= {field_name.capitalize()}\n\n" + asciidoc_content = f"== {field_name}\n\n" field_data = schema['properties'][field_name] asciidoc_content += field_data.get("description", "") + "\n\n" # Generate the content for the properties, recursively handling nested properties if 'properties' in field_data: - asciidoc_content += generate_asciidoc_properties(field_data['properties'], required_fields, level=2) + asciidoc_content += generate_asciidoc_properties(field_data['properties'], required_fields, level=3) elif field_data.get('type') == 'array': # Handle array fields directly if 'items' in field_data and isinstance(field_data['items'], dict) and 'items' in field_data['items']: @@ -134,9 +151,9 @@ def main(): Main function to handle command-line arguments, process the JSON schema, and generate the AsciiDoc documentation. """ # Set up argument parser - parser = argparse.ArgumentParser(description="Generate AsciiDoc documentation for a given JSON schema field.") + parser = argparse.ArgumentParser(description="Generate AsciiDoc documentation for a given JSON schema field or the entire schema.") parser.add_argument('json_schema_path', type=str, help="Path to the JSON schema file.") - parser.add_argument('field_name', type=str, help="Name of the field (e.g., metadata) to generate documentation for.") + parser.add_argument('field_name', type=str, nargs='?', default='', help="Name of the field (e.g., metadata) to generate documentation for. Leave empty to generate documentation for the entire schema.") # Parse the arguments args = parser.parse_args() @@ -147,19 +164,40 @@ def main(): with open(json_schema_path, 'r') as file: schema = json.load(file) - # Check if the field exists in the schema - if field_name not in schema['properties']: - print(f"Error: The field '{field_name}' does not exist in the provided schema.") - return + # Generate documentation for the entire schema if field_name is empty + if not field_name: + # Generate AsciiDoc for all fields + print("Generating AsciiDoc for the entire schema...") + + # Generate the main headline from the file name + base_filename = os.path.basename(json_schema_path).replace('_', '-') + headline = format_main_headline(os.path.splitext(base_filename)[0]) + asciidoc_content = f"= {headline}\n\n" + + # Process each field in the schema + for field in schema['properties']: + required_fields = schema['properties'][field].get('required', []) + asciidoc_content += generate_asciidoc(field, schema, required_fields) + + # Save the AsciiDoc content to a file + base_filename = os.path.basename(json_schema_path).replace('_', '-') + output_filename = f"{os.path.splitext(base_filename)[0]}.adoc" + else: + # Check if the field exists in the schema + if field_name not in schema['properties']: + print(f"Error: The field '{field_name}' does not exist in the provided schema.") + return + + # Get the required fields for the selected field + required_fields = schema['properties'][field_name].get('required', []) - # Get the required fields for the selected field - required_fields = schema['properties'][field_name].get('required', []) + # Generate the AsciiDoc content for the specific field + asciidoc_content = generate_asciidoc(field_name, schema, required_fields) - # Generate the AsciiDoc content - asciidoc_content = generate_asciidoc(field_name, schema, required_fields) + # Save the AsciiDoc content to a file + output_filename = f"{field_name}.adoc" - # Save the AsciiDoc content to a file - output_filename = f"{field_name}.adoc" + # Write the output to a file with open(output_filename, 'w') as file: file.write(asciidoc_content) diff --git a/scripts/metadata.adoc b/scripts/metadata.adoc new file mode 100644 index 00000000..05a93470 --- /dev/null +++ b/scripts/metadata.adoc @@ -0,0 +1,140 @@ +== metadata + +Metadata about the asset. + +=== name +Name of the asset. + +*Required:* Yes + +=== description +Short description of the asset in 2 - 3 sentences. + +*Required:* Yes + +=== uuid +Universally unique identifier for the asset. + +*Pattern:* `^[a-f0-9]\{32\}$` + +*Required:* Yes + +=== assetVersion +Version of the asset. + +*Pattern:* `^\\d+\\.\\d+\\.\\d+$` + +*Required:* Yes + +=== openMaterialVersion +Version of OpenMATERIAL. + +*Pattern:* `^\\d+\\.\\d+\\.\\d+$` + +*Required:* Yes + +=== copyright +Copyright information with year and company. + +*Required:* Yes + +=== license +License information. For common open source licenses, provide an SPDX identifier. For other types of licenses, provide an URL to a webpage with the license or the filename of a separately provided license file. + +*Required:* Yes + +=== author +Name or email address of the author of this asset. In case of multiple authors, use comma-separation. The author can also be a company name. + +*Required:* Yes + +=== modelCreationTool +The tool or software used to generate the 3D model of this asset. + +*Required:* No + +=== creationDate +Creation date of the asset in the format YYYYMMDDTHHMMSSZ. + +*Pattern:* `^\\d\{8\}T\\d\{6\}Z$` + +*Required:* Yes + +=== modelingMethod +Description how the 3D model was modeled. Was it modeled based on a 3D scan, based on photos, or freehand? + +*Required:* No + +=== validationDescription +Was the geometry of the 3D model validated against a real-world object? If yes, how? + +*Required:* No + +=== assetType +Type of the asset out of the following: object (individual object), scene (a collection of multiple objects) + +*Required:* Yes + +=== objectClass +The class of the object. This only applies if 'asset_type' == 'object'. + +*Required:* Yes + +=== animated +Does the 3D model contain keyframe animations? + +*Required:* Yes + +=== pbrMaterialWorkflow +Does the 3D model contain materials for a PBR workflow? If yes, for a metallic or specular workflow? + +*Required:* Yes + +=== triangleCount +The number of triangles in the 3D model. One quad counts as two triangles. + +*Required:* Yes + +=== meshCount +The number of meshes in the 3D geometry. + +*Required:* Yes + +=== textureResolutions +List of all texture resolutions in the 3D model. This can include any combination of '1K', '2K', or '4K'. + +No description + +*Required:* Yes + +=== normalMapFormat +Does the 3D model have normal maps as textures? If yes, are they using the DirectX format or the OpenGL format? + +*Required:* Yes + +=== boundingBox +Axis-aligned bounding box dimensions specifying the minimum and maximum values in x, y, and z direction. + +*Required:* Yes + +==== x +Minimum and maximum values in m in x direction. + +No description + +*Required:* Yes + +==== y +Minimum and maximum values in m in y direction. + +No description + +*Required:* Yes + +==== z +Minimum and maximum values in m in z direction. + +No description + +*Required:* Yes +