forked from bartvdbraak/blender
Render: new material preview
* EEVEE support through irradiance volume and light probe. * New shader ball shape (designed by Robin Marin). * New cloth and liquid shapes, removed monkey. * Replace world sphere by toggle to use world for any shape. * Slight bevel on cube. * More subdivision for displacement preview. * Fixed and improved UV mapping for all shapes. * Material icon / asset preview now uses specified shape instead of always a sphere. So for example hair material can be displayed as hair. Ref T57683
This commit is contained in:
parent
8b0102b443
commit
f5ea1fc4fb
BIN
release/datafiles/preview.blend
Normal file
BIN
release/datafiles/preview.blend
Normal file
Binary file not shown.
Binary file not shown.
@ -767,7 +767,7 @@ if(WITH_BLENDER)
|
|||||||
# blender UI only
|
# blender UI only
|
||||||
|
|
||||||
# blends
|
# blends
|
||||||
data_to_c_simple(../../../../release/datafiles/preview_cycles.blend SRC)
|
data_to_c_simple(../../../../release/datafiles/preview.blend SRC)
|
||||||
data_to_c_simple(../../../../release/datafiles/preview_grease_pencil.blend SRC)
|
data_to_c_simple(../../../../release/datafiles/preview_grease_pencil.blend SRC)
|
||||||
|
|
||||||
# images
|
# images
|
||||||
|
@ -29,8 +29,8 @@
|
|||||||
extern int datatoc_startup_blend_size;
|
extern int datatoc_startup_blend_size;
|
||||||
extern char datatoc_startup_blend[];
|
extern char datatoc_startup_blend[];
|
||||||
|
|
||||||
extern int datatoc_preview_cycles_blend_size;
|
extern int datatoc_preview_blend_size;
|
||||||
extern char datatoc_preview_cycles_blend[];
|
extern char datatoc_preview_blend[];
|
||||||
|
|
||||||
extern int datatoc_preview_grease_pencil_blend_size;
|
extern int datatoc_preview_grease_pencil_blend_size;
|
||||||
extern char datatoc_preview_grease_pencil_blend[];
|
extern char datatoc_preview_grease_pencil_blend[];
|
||||||
|
@ -2215,7 +2215,7 @@ void uiTemplatePreview(
|
|||||||
if (!ui_preview) {
|
if (!ui_preview) {
|
||||||
ui_preview = MEM_callocN(sizeof(uiPreview), "uiPreview");
|
ui_preview = MEM_callocN(sizeof(uiPreview), "uiPreview");
|
||||||
BLI_strncpy(ui_preview->preview_id, preview_id, sizeof(ui_preview->preview_id));
|
BLI_strncpy(ui_preview->preview_id, preview_id, sizeof(ui_preview->preview_id));
|
||||||
ui_preview->height = (short)(UI_UNIT_Y * 5.6f);
|
ui_preview->height = (short)(UI_UNIT_Y * 7.6f);
|
||||||
BLI_addtail(&ar->ui_previews, ui_preview);
|
BLI_addtail(&ar->ui_previews, ui_preview);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2257,6 +2257,8 @@ void uiTemplatePreview(
|
|||||||
col = uiLayoutColumn(row, true);
|
col = uiLayoutColumn(row, true);
|
||||||
uiLayoutSetScaleX(col, 1.5);
|
uiLayoutSetScaleX(col, 1.5);
|
||||||
uiItemR(col, &material_ptr, "preview_render_type", UI_ITEM_R_EXPAND, "", ICON_NONE);
|
uiItemR(col, &material_ptr, "preview_render_type", UI_ITEM_R_EXPAND, "", ICON_NONE);
|
||||||
|
uiItemS(col);
|
||||||
|
uiItemR(col, &material_ptr, "use_preview_world", 0, "", ICON_WORLD);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pr_texture) {
|
if (pr_texture) {
|
||||||
|
@ -189,7 +189,7 @@ typedef struct IconPreview {
|
|||||||
|
|
||||||
/* *************************** Preview for buttons *********************** */
|
/* *************************** Preview for buttons *********************** */
|
||||||
|
|
||||||
static Main *G_pr_main_cycles = NULL;
|
static Main *G_pr_main = NULL;
|
||||||
static Main *G_pr_main_grease_pencil = NULL;
|
static Main *G_pr_main_grease_pencil = NULL;
|
||||||
|
|
||||||
#ifndef WITH_HEADLESS
|
#ifndef WITH_HEADLESS
|
||||||
@ -218,19 +218,13 @@ void ED_preview_ensure_dbase(void)
|
|||||||
static bool base_initialized = false;
|
static bool base_initialized = false;
|
||||||
BLI_assert(BLI_thread_is_main());
|
BLI_assert(BLI_thread_is_main());
|
||||||
if (!base_initialized) {
|
if (!base_initialized) {
|
||||||
G_pr_main_cycles = load_main_from_memory(datatoc_preview_cycles_blend, datatoc_preview_cycles_blend_size);
|
G_pr_main = load_main_from_memory(datatoc_preview_blend, datatoc_preview_blend_size);
|
||||||
G_pr_main_grease_pencil = load_main_from_memory(datatoc_preview_grease_pencil_blend, datatoc_preview_grease_pencil_blend_size);
|
G_pr_main_grease_pencil = load_main_from_memory(datatoc_preview_grease_pencil_blend, datatoc_preview_grease_pencil_blend_size);
|
||||||
base_initialized = true;
|
base_initialized = true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool check_engine_supports_textures(Scene *scene)
|
|
||||||
{
|
|
||||||
RenderEngineType *type = RE_engines_find(scene->r.engine);
|
|
||||||
return (type->flag & RE_USE_TEXTURE_PREVIEW) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool check_engine_supports_preview(Scene *scene)
|
static bool check_engine_supports_preview(Scene *scene)
|
||||||
{
|
{
|
||||||
RenderEngineType *type = RE_engines_find(scene->r.engine);
|
RenderEngineType *type = RE_engines_find(scene->r.engine);
|
||||||
@ -239,8 +233,8 @@ static bool check_engine_supports_preview(Scene *scene)
|
|||||||
|
|
||||||
void ED_preview_free_dbase(void)
|
void ED_preview_free_dbase(void)
|
||||||
{
|
{
|
||||||
if (G_pr_main_cycles)
|
if (G_pr_main)
|
||||||
BKE_main_free(G_pr_main_cycles);
|
BKE_main_free(G_pr_main);
|
||||||
|
|
||||||
if (G_pr_main_grease_pencil)
|
if (G_pr_main_grease_pencil)
|
||||||
BKE_main_free(G_pr_main_grease_pencil);
|
BKE_main_free(G_pr_main_grease_pencil);
|
||||||
@ -262,14 +256,16 @@ static const char *preview_collection_name(const char pr_type)
|
|||||||
return "Sphere";
|
return "Sphere";
|
||||||
case MA_CUBE:
|
case MA_CUBE:
|
||||||
return "Cube";
|
return "Cube";
|
||||||
case MA_MONKEY:
|
case MA_SHADERBALL:
|
||||||
return "Monkey";
|
return "Shader Ball";
|
||||||
|
case MA_CLOTH:
|
||||||
|
return "Cloth";
|
||||||
|
case MA_FLUID:
|
||||||
|
return "Fluid";
|
||||||
case MA_SPHERE_A:
|
case MA_SPHERE_A:
|
||||||
return "World Sphere";
|
return "World Shader Ball";
|
||||||
case MA_TEXTURE:
|
|
||||||
return "Texture";
|
|
||||||
case MA_LAMP:
|
case MA_LAMP:
|
||||||
return "Light";
|
return "Lamp";
|
||||||
case MA_SKY:
|
case MA_SKY:
|
||||||
return "Sky";
|
return "Sky";
|
||||||
case MA_HAIR:
|
case MA_HAIR:
|
||||||
@ -282,8 +278,9 @@ static const char *preview_collection_name(const char pr_type)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_preview_collection(Scene *scene, ViewLayer *view_layer, char pr_type)
|
static void set_preview_visibility(Scene *scene, ViewLayer *view_layer, char pr_type, int pr_method)
|
||||||
{
|
{
|
||||||
|
/* Set appropriate layer as visibile. */
|
||||||
LayerCollection *lc = view_layer->layer_collections.first;
|
LayerCollection *lc = view_layer->layer_collections.first;
|
||||||
const char *collection_name = preview_collection_name(pr_type);
|
const char *collection_name = preview_collection_name(pr_type);
|
||||||
|
|
||||||
@ -296,6 +293,18 @@ static void set_preview_collection(Scene *scene, ViewLayer *view_layer, char pr_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Hide floor for icon renders. */
|
||||||
|
for (Base *base = view_layer->object_bases.first; base; base = base->next) {
|
||||||
|
if (STREQ(base->object->id.name + 2, "Floor")) {
|
||||||
|
if (pr_method == PR_ICON_RENDER) {
|
||||||
|
base->object->restrictflag |= OB_RESTRICT_RENDER;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
base->object->restrictflag &= ~OB_RESTRICT_RENDER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BKE_layer_collection_sync(scene, view_layer);
|
BKE_layer_collection_sync(scene, view_layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,11 +398,8 @@ static Scene *preview_prepare_scene(Main *bmain, Scene *scene, ID *id, int id_ty
|
|||||||
|
|
||||||
sce->r.cfra = scene->r.cfra;
|
sce->r.cfra = scene->r.cfra;
|
||||||
|
|
||||||
if (id_type == ID_TE && !check_engine_supports_textures(scene)) {
|
if (id_type == ID_TE) {
|
||||||
/* Force blender internal for texture icons and nodes render,
|
/* Texture is not actually rendered with engine, just set dummy value. */
|
||||||
* seems commonly used render engines does not support
|
|
||||||
* such kind of rendering.
|
|
||||||
*/
|
|
||||||
BLI_strncpy(sce->r.engine, RE_engine_id_BLENDER_EEVEE, sizeof(sce->r.engine));
|
BLI_strncpy(sce->r.engine, RE_engine_id_BLENDER_EEVEE, sizeof(sce->r.engine));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -410,8 +416,8 @@ static Scene *preview_prepare_scene(Main *bmain, Scene *scene, ID *id, int id_ty
|
|||||||
sp->id_copy = NULL;
|
sp->id_copy = NULL;
|
||||||
BLI_addtail(&pr_main->materials, mat);
|
BLI_addtail(&pr_main->materials, mat);
|
||||||
|
|
||||||
/* use current scene world to light sphere */
|
/* Use current scene world for lighting. */
|
||||||
if (mat->pr_type == MA_SPHERE_A && sp->pr_method == PR_BUTS_RENDER) {
|
if (mat->pr_flag == MA_PREVIEW_WORLD && sp->pr_method == PR_BUTS_RENDER) {
|
||||||
/* Use current scene world to light sphere. */
|
/* Use current scene world to light sphere. */
|
||||||
sce->world = preview_get_localized_world(sp, scene->world);
|
sce->world = preview_get_localized_world(sp, scene->world);
|
||||||
}
|
}
|
||||||
@ -419,17 +425,14 @@ static Scene *preview_prepare_scene(Main *bmain, Scene *scene, ID *id, int id_ty
|
|||||||
/* Use a default world color. Using the current
|
/* Use a default world color. Using the current
|
||||||
* scene world can be slow if it has big textures. */
|
* scene world can be slow if it has big textures. */
|
||||||
sce->world->use_nodes = false;
|
sce->world->use_nodes = false;
|
||||||
sce->world->horr = 0.5f;
|
sce->world->horr = 0.05f;
|
||||||
sce->world->horg = 0.5f;
|
sce->world->horg = 0.05f;
|
||||||
sce->world->horb = 0.5f;
|
sce->world->horb = 0.05f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sp->pr_method == PR_ICON_RENDER) {
|
set_preview_visibility(sce, view_layer, mat->pr_type, sp->pr_method);
|
||||||
set_preview_collection(sce, view_layer, MA_SPHERE_A);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
set_preview_collection(sce, view_layer, mat->pr_type);
|
|
||||||
|
|
||||||
|
if (sp->pr_method != PR_ICON_RENDER) {
|
||||||
if (mat->nodetree && sp->pr_method == PR_NODE_RENDER) {
|
if (mat->nodetree && sp->pr_method == PR_NODE_RENDER) {
|
||||||
/* two previews, they get copied by wmJob */
|
/* two previews, they get copied by wmJob */
|
||||||
BKE_node_preview_init_tree(mat->nodetree, sp->sizex, sp->sizey, true);
|
BKE_node_preview_init_tree(mat->nodetree, sp->sizex, sp->sizey, true);
|
||||||
@ -470,7 +473,6 @@ static Scene *preview_prepare_scene(Main *bmain, Scene *scene, ID *id, int id_ty
|
|||||||
sp->id_copy = NULL;
|
sp->id_copy = NULL;
|
||||||
BLI_addtail(&pr_main->textures, tex);
|
BLI_addtail(&pr_main->textures, tex);
|
||||||
}
|
}
|
||||||
set_preview_collection(sce, view_layer, MA_TEXTURE);
|
|
||||||
|
|
||||||
if (tex && tex->nodetree && sp->pr_method == PR_NODE_RENDER) {
|
if (tex && tex->nodetree && sp->pr_method == PR_NODE_RENDER) {
|
||||||
/* two previews, they get copied by wmJob */
|
/* two previews, they get copied by wmJob */
|
||||||
@ -490,7 +492,7 @@ static Scene *preview_prepare_scene(Main *bmain, Scene *scene, ID *id, int id_ty
|
|||||||
BLI_addtail(&pr_main->lights, la);
|
BLI_addtail(&pr_main->lights, la);
|
||||||
}
|
}
|
||||||
|
|
||||||
set_preview_collection(sce, view_layer, MA_LAMP);
|
set_preview_visibility(sce, view_layer, MA_LAMP, sp->pr_method);
|
||||||
|
|
||||||
if (sce->world) {
|
if (sce->world) {
|
||||||
/* Only use lighting from the light. */
|
/* Only use lighting from the light. */
|
||||||
@ -524,7 +526,7 @@ static Scene *preview_prepare_scene(Main *bmain, Scene *scene, ID *id, int id_ty
|
|||||||
BLI_addtail(&pr_main->worlds, wrld);
|
BLI_addtail(&pr_main->worlds, wrld);
|
||||||
}
|
}
|
||||||
|
|
||||||
set_preview_collection(sce, view_layer, MA_SKY);
|
set_preview_visibility(sce, view_layer, MA_SKY, sp->pr_method);
|
||||||
sce->world = wrld;
|
sce->world = wrld;
|
||||||
|
|
||||||
if (wrld && wrld->nodetree && sp->pr_method == PR_NODE_RENDER) {
|
if (wrld && wrld->nodetree && sp->pr_method == PR_NODE_RENDER) {
|
||||||
@ -1178,7 +1180,7 @@ static void icon_preview_startjob_all_sizes(void *customdata, short *stop, short
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((ma == NULL) || (ma->gp_style == NULL)) {
|
if ((ma == NULL) || (ma->gp_style == NULL)) {
|
||||||
sp->pr_main = G_pr_main_cycles;
|
sp->pr_main = G_pr_main;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
sp->pr_main = G_pr_main_grease_pencil;
|
sp->pr_main = G_pr_main_grease_pencil;
|
||||||
@ -1359,7 +1361,7 @@ void ED_preview_shader_job(const bContext *C, void *owner, ID *id, ID *parent, M
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((ma == NULL) || (ma->gp_style == NULL)) {
|
if ((ma == NULL) || (ma->gp_style == NULL)) {
|
||||||
sp->pr_main = G_pr_main_cycles;
|
sp->pr_main = G_pr_main;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
sp->pr_main = G_pr_main_grease_pencil;
|
sp->pr_main = G_pr_main_grease_pencil;
|
||||||
|
@ -147,11 +147,14 @@ typedef struct Material {
|
|||||||
float gloss_mir DNA_DEPRECATED;
|
float gloss_mir DNA_DEPRECATED;
|
||||||
float roughness;
|
float roughness;
|
||||||
float metallic;
|
float metallic;
|
||||||
char _pad0[2];
|
|
||||||
|
|
||||||
/** For buttons and render. */
|
/** Nodes */
|
||||||
char pr_type, use_nodes;
|
char use_nodes;
|
||||||
|
|
||||||
|
/** Preview render. */
|
||||||
|
char pr_type;
|
||||||
short pr_texture;
|
short pr_texture;
|
||||||
|
short pr_flag;
|
||||||
|
|
||||||
/** Index for render passes. */
|
/** Index for render passes. */
|
||||||
short index;
|
short index;
|
||||||
@ -279,13 +282,18 @@ typedef struct Material {
|
|||||||
#define MA_FLAT 0
|
#define MA_FLAT 0
|
||||||
#define MA_SPHERE 1
|
#define MA_SPHERE 1
|
||||||
#define MA_CUBE 2
|
#define MA_CUBE 2
|
||||||
#define MA_MONKEY 3
|
#define MA_SHADERBALL 3
|
||||||
#define MA_SPHERE_A 4
|
#define MA_SPHERE_A 4 /* Used for icon renders only. */
|
||||||
#define MA_TEXTURE 5
|
#define MA_TEXTURE 5
|
||||||
#define MA_LAMP 6
|
#define MA_LAMP 6
|
||||||
#define MA_SKY 7
|
#define MA_SKY 7
|
||||||
#define MA_HAIR 10
|
#define MA_HAIR 10
|
||||||
#define MA_ATMOS 11
|
#define MA_ATMOS 11
|
||||||
|
#define MA_CLOTH 12
|
||||||
|
#define MA_FLUID 13
|
||||||
|
|
||||||
|
/* pr_flag */
|
||||||
|
#define MA_PREVIEW_WORLD (1 << 0)
|
||||||
|
|
||||||
/* blend_method */
|
/* blend_method */
|
||||||
enum {
|
enum {
|
||||||
|
@ -658,9 +658,10 @@ void RNA_def_material(BlenderRNA *brna)
|
|||||||
{MA_FLAT, "FLAT", ICON_MATPLANE, "Flat", "Flat XY plane"},
|
{MA_FLAT, "FLAT", ICON_MATPLANE, "Flat", "Flat XY plane"},
|
||||||
{MA_SPHERE, "SPHERE", ICON_MATSPHERE, "Sphere", "Sphere"},
|
{MA_SPHERE, "SPHERE", ICON_MATSPHERE, "Sphere", "Sphere"},
|
||||||
{MA_CUBE, "CUBE", ICON_MATCUBE, "Cube", "Cube"},
|
{MA_CUBE, "CUBE", ICON_MATCUBE, "Cube", "Cube"},
|
||||||
{MA_MONKEY, "MONKEY", ICON_MONKEY, "Monkey", "Monkey"},
|
|
||||||
{MA_HAIR, "HAIR", ICON_HAIR, "Hair", "Hair strands"},
|
{MA_HAIR, "HAIR", ICON_HAIR, "Hair", "Hair strands"},
|
||||||
{MA_SPHERE_A, "SPHERE_A", ICON_MAT_SPHERE_SKY, "World Sphere", "Large sphere with sky"},
|
{MA_SHADERBALL, "SHADERBALL", ICON_MATSHADERBALL, "Shader Ball", "Shader Ball"},
|
||||||
|
{MA_CLOTH, "CLOTH", ICON_MATCLOTH, "Cloth", "Cloth"},
|
||||||
|
{MA_FLUID, "FLUID", ICON_MATFLUID, "Fluid", "Fluid"},
|
||||||
{0, NULL, 0, NULL, NULL},
|
{0, NULL, 0, NULL, NULL},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -731,7 +732,12 @@ void RNA_def_material(BlenderRNA *brna)
|
|||||||
prop = RNA_def_property(srna, "preview_render_type", PROP_ENUM, PROP_NONE);
|
prop = RNA_def_property(srna, "preview_render_type", PROP_ENUM, PROP_NONE);
|
||||||
RNA_def_property_enum_sdna(prop, NULL, "pr_type");
|
RNA_def_property_enum_sdna(prop, NULL, "pr_type");
|
||||||
RNA_def_property_enum_items(prop, preview_type_items);
|
RNA_def_property_enum_items(prop, preview_type_items);
|
||||||
RNA_def_property_ui_text(prop, "Preview render type", "Type of preview render");
|
RNA_def_property_ui_text(prop, "Preview Render Type", "Type of preview render");
|
||||||
|
RNA_def_property_update(prop, 0, "rna_Material_update_previews");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "use_preview_world", PROP_BOOLEAN, PROP_NONE);
|
||||||
|
RNA_def_property_boolean_sdna(prop, NULL, "pr_flag", MA_PREVIEW_WORLD);
|
||||||
|
RNA_def_property_ui_text(prop, "Preview World", "Use the current world background to light the preview render");
|
||||||
RNA_def_property_update(prop, 0, "rna_Material_update_previews");
|
RNA_def_property_update(prop, 0, "rna_Material_update_previews");
|
||||||
|
|
||||||
prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_UNSIGNED);
|
prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_UNSIGNED);
|
||||||
|
@ -744,10 +744,6 @@ static void rna_def_render_engine(BlenderRNA *brna)
|
|||||||
RNA_def_property_boolean_sdna(prop, NULL, "type->flag", RE_USE_PREVIEW);
|
RNA_def_property_boolean_sdna(prop, NULL, "type->flag", RE_USE_PREVIEW);
|
||||||
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
|
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
|
||||||
|
|
||||||
prop = RNA_def_property(srna, "bl_use_texture_preview", PROP_BOOLEAN, PROP_NONE);
|
|
||||||
RNA_def_property_boolean_sdna(prop, NULL, "type->flag", RE_USE_TEXTURE_PREVIEW);
|
|
||||||
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
|
|
||||||
|
|
||||||
prop = RNA_def_property(srna, "bl_use_postprocess", PROP_BOOLEAN, PROP_NONE);
|
prop = RNA_def_property(srna, "bl_use_postprocess", PROP_BOOLEAN, PROP_NONE);
|
||||||
RNA_def_property_boolean_negative_sdna(prop, NULL, "type->flag", RE_USE_POSTPROCESS);
|
RNA_def_property_boolean_negative_sdna(prop, NULL, "type->flag", RE_USE_POSTPROCESS);
|
||||||
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
|
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
|
||||||
|
@ -58,7 +58,6 @@ struct bNodeTree;
|
|||||||
#define RE_USE_SHADING_NODES 16
|
#define RE_USE_SHADING_NODES 16
|
||||||
#define RE_USE_EXCLUDE_LAYERS 32
|
#define RE_USE_EXCLUDE_LAYERS 32
|
||||||
#define RE_USE_SAVE_BUFFERS 64
|
#define RE_USE_SAVE_BUFFERS 64
|
||||||
#define RE_USE_TEXTURE_PREVIEW 128
|
|
||||||
#define RE_USE_SHADING_NODES_CUSTOM 256
|
#define RE_USE_SHADING_NODES_CUSTOM 256
|
||||||
#define RE_USE_SPHERICAL_STEREO 512
|
#define RE_USE_SPHERICAL_STEREO 512
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user