.paq Specification
.paq
is a file format we use to ship asset packs. It is a ZIP archive with a specific predefined
content structure and .paq
extension. We use the .paq
extension for a better user experience
with the Blender's file selector.
engon
is able to install any .paq
file that follows the given structure and automatically
displays all found assets in the browser. Alternatively a folder containing extracted .paq
file with
.pack-info
can be registered into the browser using search paths. Check
search paths for more information. This is a good option for development, as you
don't have to archive the contents into .paq
each time.
Specification is up to date with engon 1.4.0
This specification was written based on engon 1.4.0
. engon
is always backwards compatible
and will load the index even from previous versions. Packs made according to this specification
will load in all future engon
versions.
.paq file structure¶
pack_name.paq
├── blends/
├── previews/
├── textures/ [optional]
├── fonts/ [optional]
├── sequences/ [optional]
├── icons/
│ ├── vendor.png [optional]
│ └── pack.png [optional]
├── index.json
└── pack_name.pack-info
Field | Description |
---|---|
blends/ |
Directory with .blend files, can contain nested folders for categories. Can contain arbitrary .blends , but he ones referenced in index.json can be spawned and have to follow Asset Conventions. |
previews/ |
Directory with .png preview images for the assets, ideally the folder structure matches the blends/ , but it's not a hard requirement. |
textures/ |
Optional Directory with texture files used by the assets. |
fonts/ |
Optional Directory with .ttf files used by the assets. |
sequences/ |
Optional directory with sequence files named according to Blender's naming convention. |
icons/ |
Optional directory with .png icon files for the pack and vendor. |
index.json |
One or more JSON files containing metadata and information about the assets. See index.json. |
pack_name.pack-info |
JSON like file containing information about the asset pack. See .pack-info. |
Recommended folder structure
This folder structure is what we use and we recommend it based on our experience. However any other
structure will most likely work as well as long as the paths in index.json
are relative to
the root of the pack.
Packaging to .paq
To create a .paq
out of your folder hierarchy use your favorite tool to create a ZIP
archive of the contents and rename the .zip
to .paq
.
Paths have to be relative and encapsulated
In order to ship a pack with all dependencies without missing textures, libraries and
dependencies, all the paths have to be made relative within the .paq
hierarchy.
If the .paq
is shipped with textures pointing to some local directory using absolute path,
then this path will most likely not be available on the user's device, as the pack can be
installed in arbitrary location.
.pack-info file¶
Contains information about the given pack. Structure:
{
"full_name": "pack_name",
"version": [1, 0, 0],
"vendor": "vendor_name",
"engon_features": ["aquatiq"],
"min_engon_version": [1, 3, 0],
"index_paths": ["index.json"],
"file_id_prefix": "/pack_name",
"pack_icon": "icons/pack_icon.png",
"vendor_icon": "icons/vendor_icon.png"
}
Field | Description |
---|---|
full_name |
Full name of the asset pack. |
version |
Version of the asset pack in [major, minor, patch] format according to semantic versioning. |
vendor |
Name of the vendor providing the asset pack. |
engon_features |
A list of engon features to enable when the asset pack is installed (can be empty, this is mostly tied to polygoniq features). |
min_engon_version |
The minimum version of engon required to use the asset pack. |
index_paths |
A list of paths to index.json files within the asset pack. |
file_id_prefix |
A prefix used to make sure file paths are unique within our addon. |
pack_icon |
Optional path to the icon for the pack ("null" for empty) |
vendor_icon |
Optional path to the icon for the vendor ("null" for empty). |
index.json¶
This is the heart of the asset pack. It provides information about assets, categories and other. Minimal working set of fields is:
{
"asset_data": {
"$ASSET_DATA_ID": {
"dependency_files": [
"$FILE_ID_PREFIX:ROOT_RELPATH_TO_DEPENDENCY",
...
],
"primary_blend_file": "$FILE_ID_PREFIX:ROOT_RELPATH_TO_BLEND",
"type": "$ASSET_DATA_TYPE"
},
...
},
"asset_metadata": {
"$ASSET_ID": {
"title": "$ASSET_TITLE",
"type": "$ASSET_DATA_TYPE",
"preview_file: "$FILE_ID_PREFIX:$ROOT_RELPATH_TO_PREVIEW.png",
"numeric_parameters": {...},
"tags": {...},
"text_parameters": {
"bpy.data.version": "$BPY_DATA_VERSION",
"mapr_asset_id": "$ASSET_ID",
...
},
"vector_parameters": {...}
},
...
},
"category_metadata": {
"/": {
"title": "$CATEGORY_TITLE"
},
"$CATEGORY_PATH": {
"title": "$CATEGORY_TITLE"
},
"$CATEGORY_PATH": {
"title": "$CATEGORY_TITLE"
},
...
},
"child_asset_data": {
"$ASSET_ID": [
"$ASSET_DATA_ID",
...
],
...
},
"child_assets": {
"$CATEGORY_PATH": [
"$ASSET_ID",
...
],
...
},
"child_categories": {
"$CATEGORY_PATH": [
"$CATEGORY_PATH",
...
]
}
}
Field | Description |
---|---|
$ASSET_ID |
Unique identifier for the asset. Use UUID version 4 according to RFC4122 in string representation. For example, we use uuid.uuid4() . |
$ASSET_DATA_ID |
Unique identifier for the asset data. Use UUID version 4 according to RFC4122 in string representation. For example, we use uuid.uuid4() . For most use-cases, every asset will have exactly one $ASSET_ID and exactly one $ASSET_DATA_ID . We don't support spawning multiple $ASSET_DATA_ID s yet. |
$FILE_ID_PREFIX |
Prefix used for all paths within mapr index. Has to start with / and end with : . This should be equal to the prefix provided in .pack-info file. For example /botaniq: |
$ROOT_RELPATH_TO_DEPENDENCY |
Relative path from the root of the pack to the dependency file - can be other .blend , .jpg , .png or anything that Blender allows as dependency. |
$ROOT_RELPATH_TO_BLEND |
Relative path from the root of the pack to the primary .blend file - the file where we spawn the asset from. |
$ASSET_DATA_TYPE |
Type of the asset data. Values: {blender_model , blender_world , blender_geometry_nodes , blender_particle_system , blender_material , blender_scene } |
$ASSET_TITLE |
Title of the asset - this will be displayed in the browser |
$ROOT_RELPATH_TO_PREVIEW.png |
Relative path from the root of the pack to the preview image .png file. |
$BPY_DATA_VERSION |
Version of the Blender data in string representation, e.g "3.6.13" - This is the version of the Blender data - run bpy.data.version in Blender's console to obtain it. This value is different from the Blender version. |
$CATEGORY_TITLE |
Title of the category - this will be displayed in the browser. |
$CATEGORY_PATH |
Path to the category, has to start with root (/ ). |
Example index.json
{
"asset_data": {
"57626a41-dafe-4464-a9c0-5f544eb7135e": {
"dependency_files": [
"/botaniq:blends/models/bq_Library_Materials.blend",
"/botaniq:blends/models/vine/bq_Vine_Vitis-vinifera_A_spring-summer.blend",
"/botaniq:blends/models/vine/bq_Vine_Vitis-vinifera_D_spring-summer.blend",
"/botaniq:textures/bq_Leaf_Ivy_Diffuse.png",
"/botaniq:textures/bq_Leaf_Ivy_Normal.jpg",
"/botaniq:textures/bq_Stem_Ivy_Diffuse.png",
"/botaniq:textures/bq_Stem_Ivy_Normal.jpg"
],
"primary_blend_file": "/botaniq:blends/particles/vines/bq_pps_Vines_Basic_A_spring-summer.blend",
"type": "blender_particle_system"
},
"8097b7aa-5ba7-4f4d-bd51-19cbe8edee63": {
"dependency_files": [
"/botaniq:blends/models/bq_Library_Materials.blend",
"/botaniq:textures/bq_Leaf_Ivy_Diffuse.png",
"/botaniq:textures/bq_Leaf_Ivy_Normal.jpg",
"/botaniq:textures/bq_Stem_Ivy_Diffuse.png",
"/botaniq:textures/bq_Stem_Ivy_Normal.jpg"
],
"primary_blend_file": "/botaniq:blends/models/vine/bq_Vine_Vitis-vinifera_A_spring-summer.blend",
"type": "blender_model"
},
"d6007dde-6539-41bb-88d7-bf8a5f57acd2": {
"dependency_files": [
"/botaniq:blends/models/bq_Library_Materials.blend",
"/botaniq:blends/models/vine/bq_Vine_Vitis-vinifera_A_spring-summer.blend",
"/botaniq:textures/bq_Leaf_Ivy_Diffuse.png",
"/botaniq:textures/bq_Leaf_Ivy_Normal.jpg",
"/botaniq:textures/bq_Stem_Ivy_Diffuse.png",
"/botaniq:textures/bq_Stem_Ivy_Normal.jpg"
],
"primary_blend_file": "/botaniq:blends/models/vine/bq_Vine_Vitis-vinifera_D_spring-summer.blend",
"type": "blender_model"
},
"dcd46b6b-39c5-48cb-acf1-7bc573093369": {
"dependency_files": [
"/botaniq:blends/models/bq_Library_Materials.blend",
"/botaniq:blends/models/vine/bq_Vine_Vitis-vinifera_A_spring-summer.blend",
"/botaniq:blends/models/vine/bq_Vine_Vitis-vinifera_D_spring-summer.blend",
"/botaniq:textures/bq_Leaf_Ivy_Diffuse.png",
"/botaniq:textures/bq_Leaf_Ivy_Normal.jpg",
"/botaniq:textures/bq_Stem_Ivy_Diffuse.png",
"/botaniq:textures/bq_Stem_Ivy_Normal.jpg"
],
"primary_blend_file": "/botaniq:blends/geonodes/vines/bq_Vines_Vitis-vinifera_A_spring-summer.blend",
"type": "blender_geometry_nodes"
}
},
"asset_metadata": {
"8a29aacb-7494-46c0-83a4-d46257b30003": {
"numeric_parameters": {},
"preview_file": "/botaniq:previews/particles/vines/bq_pps_Vines_Basic_A_spring-summer.png",
"tags": [
"Spring",
"Summer",
"variant starter",
"variant lite",
"variant full"
],
"text_parameters": {
"bpy.data.version": "3.6.13",
"copyright": "(c) 2018- polygoniq xyz s.r.o.",
"license": "Royalty Free",
"mapr_asset_id": "8a29aacb-7494-46c0-83a4-d46257b30003",
"polygoniq_addon": "botaniq"
},
"title": "pps Vines Basic A spring summer",
"type": "blender_particle_system",
"vector_parameters": {
"introduced_in": [
6,
2,
0
]
}
},
"b3276bc7-f444-4138-a03f-56c8acb5b03a": {
"numeric_parameters": {
"depth": 0.09522026777267456,
"height": 0.11390715092420578,
"image_count": 4,
"material_count": 2,
"object_count": 1,
"triangle_count": 44,
"triangle_count_applied": 44,
"width": 0.047646842896938324
},
"preview_file": "/botaniq:previews/models/vine/bq_Vine_Vitis-vinifera_A_spring-summer.png",
"tags": [
"Spring",
"Summer",
"variant starter",
"variant lite",
"variant full"
],
"text_parameters": {
"bpy.data.version": "3.6.13",
"bq_animation_type": "Wind-Simple",
"class": "Magnoliopsida",
"class_en": "Dicots",
"conservation_status": "0 - Least concern (LC)",
"copyright": "(c) 2018- polygoniq xyz s.r.o.",
"family": "Vitaceae",
"family_en": "Grape family",
"genus": "Vitis",
"genus_en": "Grapevines",
"license": "Royalty Free",
"mapr_asset_id": "b3276bc7-f444-4138-a03f-56c8acb5b03a",
"model_detail": "Low-poly",
"order": "Vitales",
"order_en": "Grapes and allies",
"polygoniq_addon": "botaniq",
"species": "Vitis vinifera",
"species_en": "Wine grape"
},
"title": "Vine Vitis vinifera A spring summer",
"type": "blender_model",
"vector_parameters": {
"introduced_in": [
6,
2,
0
]
}
},
"cd6b5586-2460-4e95-ae3e-b1cbebb1fc00": {
"numeric_parameters": {},
"preview_file": "/botaniq:previews/geonodes/vines/bq_Vines_Vitis-vinifera_A_spring-summer.png",
"tags": [
"Drawable",
"Spring",
"Summer",
"variant lite",
"variant full"
],
"text_parameters": {
"bpy.data.version": "3.6.13",
"class": "Magnoliopsida",
"class_en": "Dicots",
"conservation_status": "0 - Least concern (LC)",
"copyright": "(c) 2018- polygoniq xyz s.r.o.",
"family": "Vitaceae",
"family_en": "Grape family",
"genus": "Vitis",
"genus_en": "Grapevines",
"license": "Royalty Free",
"mapr_asset_id": "cd6b5586-2460-4e95-ae3e-b1cbebb1fc00",
"order": "Vitales",
"order_en": "Grapes and allies",
"polygoniq_addon": "botaniq",
"species": "Vitis vinifera",
"species_en": "Wine grape"
},
"title": "Vines Vitis vinifera A spring summer",
"type": "blender_geometry_nodes",
"vector_parameters": {
"introduced_in": [
7,
0,
0
]
}
},
"dde8edeb-0509-43e4-b4ad-8af939bf141d": {
"numeric_parameters": {
"depth": 0.14925122261047363,
"height": 0.15415030717849731,
"image_count": 4,
"material_count": 2,
"object_count": 1,
"triangle_count": 136,
"triangle_count_applied": 136,
"width": 0.12670491635799408
},
"preview_file": "/botaniq:previews/models/vine/bq_Vine_Vitis-vinifera_D_spring-summer.png",
"tags": [
"Spring",
"Summer",
"variant starter",
"variant lite",
"variant full"
],
"text_parameters": {
"bpy.data.version": "3.6.13",
"bq_animation_type": "Wind-Simple",
"class": "Magnoliopsida",
"class_en": "Dicots",
"conservation_status": "0 - Least concern (LC)",
"copyright": "(c) 2018- polygoniq xyz s.r.o.",
"family": "Vitaceae",
"family_en": "Grape family",
"genus": "Vitis",
"genus_en": "Grapevines",
"license": "Royalty Free",
"mapr_asset_id": "dde8edeb-0509-43e4-b4ad-8af939bf141d",
"model_detail": "Low-poly",
"order": "Vitales",
"order_en": "Grapes and allies",
"polygoniq_addon": "botaniq",
"species": "Vitis vinifera",
"species_en": "Wine grape"
},
"title": "Vine Vitis vinifera D spring summer",
"type": "blender_model",
"vector_parameters": {
"introduced_in": [
6,
2,
0
]
}
}
},
"category_metadata": {
"/": {
"title": "all"
},
"/botaniq": {
"title": "botaniq"
},
"/botaniq/vine": {
"title": "Vine"
},
"/botaniq/vines": {
"title": "Vines"
}
},
"child_asset_data": {
"8a29aacb-7494-46c0-83a4-d46257b30003": [
"57626a41-dafe-4464-a9c0-5f544eb7135e"
],
"b3276bc7-f444-4138-a03f-56c8acb5b03a": [
"8097b7aa-5ba7-4f4d-bd51-19cbe8edee63"
],
"cd6b5586-2460-4e95-ae3e-b1cbebb1fc00": [
"dcd46b6b-39c5-48cb-acf1-7bc573093369"
],
"dde8edeb-0509-43e4-b4ad-8af939bf141d": [
"d6007dde-6539-41bb-88d7-bf8a5f57acd2"
]
},
"child_assets": {
"/botaniq/vine": [
"b3276bc7-f444-4138-a03f-56c8acb5b03a",
"dde8edeb-0509-43e4-b4ad-8af939bf141d"
],
"/botaniq/vines": [
"8a29aacb-7494-46c0-83a4-d46257b30003",
"cd6b5586-2460-4e95-ae3e-b1cbebb1fc00"
]
},
"child_categories": {
"/": [
"/botaniq"
],
"/botaniq": [
"/botaniq/vine",
"/botaniq/vines"
]
}
}
Asset Conventions¶
Each asset is stored in a primary .blend
file. Individual $ASSET_DATA_TYPE
s assume different
structure of the contents of the .blend
, so we can spawn the asset consistently. All the
datablocks
marked as required for the data type need to be local in the .blend
file so
they are reachable in the API.
Spawning the asset to the scene mostly requires the relevant datablock to have the same name
as the basename of the .blend
without extension - e.g. bq_Vine_Vitis-vinifera_D_spring-summer.blend
contains collection bq_Vine_Vitis-vinifera_D_spring-summer.blend
.
Asset Data Type | Requirements |
---|---|
blender_model |
Collection (bpy.data.collections ) with the same name as the filename of the blend without extension is loaded from the file. |
blender_world |
First world (bpy.data.worlds ) is loaded. |
blender_geometry_nodes |
Object (bpy.data.objects ) with the same name as the filename of the blend without extension is loaded. This object has a modifier with the geometry nodes setup. |
blender_particle_system |
All particle systems (bpy.data.particles ) are loaded from the .blend file. Dependencies of the particle system like individual instanced assets are loaded automatically by Blender. |
blender_material |
First material (bpy.data.materials ) is loaded, or if no material is found, we try to find a mesh with the same name as the filename without extensions and load first material from it. |
blender_scene |
First scene (bpy.data.scenes ) is loaded. |