Add Chromium-only Blender WebEngine parity work
This commit is contained in:
174
blender-5.2.0/intern/cycles/app/CMakeLists.txt
Normal file
174
blender-5.2.0/intern/cycles/app/CMakeLists.txt
Normal file
@@ -0,0 +1,174 @@
|
||||
# SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#####################################################################
|
||||
# Cycles standalone executable
|
||||
#####################################################################
|
||||
|
||||
set(INC
|
||||
..
|
||||
)
|
||||
set(INC_SYS
|
||||
)
|
||||
|
||||
set(LIB
|
||||
cycles_device
|
||||
cycles_kernel
|
||||
cycles_scene
|
||||
cycles_session
|
||||
cycles_bvh
|
||||
cycles_subd
|
||||
cycles_graph
|
||||
cycles_util
|
||||
)
|
||||
|
||||
if(WITH_CYCLES_OSL)
|
||||
list(APPEND LIB cycles_kernel_osl)
|
||||
endif()
|
||||
|
||||
if(CYCLES_STANDALONE_REPOSITORY)
|
||||
list(APPEND LIB extern_sky)
|
||||
else()
|
||||
list(APPEND LIB bf_intern_sky)
|
||||
endif()
|
||||
|
||||
if(WITH_CYCLES_STANDALONE AND WITH_CYCLES_STANDALONE_GUI)
|
||||
list(APPEND LIB
|
||||
bf::dependencies::epoxy
|
||||
bf::dependencies::optional::sdl
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_USD)
|
||||
# Silence warning from USD headers using deprecated TBB header.
|
||||
add_definitions(
|
||||
-D__TBB_show_deprecation_message_atomic_H
|
||||
-D__TBB_show_deprecation_message_task_H
|
||||
)
|
||||
list(APPEND LIB
|
||||
cycles_hydra
|
||||
bf::dependencies::optional::usd
|
||||
)
|
||||
endif()
|
||||
|
||||
cycles_external_libraries_append(LIB)
|
||||
|
||||
# Common configuration.
|
||||
|
||||
include_directories(${INC})
|
||||
include_directories(SYSTEM ${INC_SYS})
|
||||
|
||||
if(APPLE AND NOT CYCLES_STANDALONE_REPOSITORY)
|
||||
set(app_output_dir Blender.app/Contents/MacOS)
|
||||
set(app_install_dir Blender.app/Contents/MacOS)
|
||||
else()
|
||||
set(app_install_dir ${CMAKE_INSTALL_PREFIX})
|
||||
endif()
|
||||
|
||||
# Application build targets
|
||||
|
||||
if(WITH_CYCLES_STANDALONE)
|
||||
set(SRC
|
||||
cycles_standalone.cpp
|
||||
cycles_xml.cpp
|
||||
cycles_xml.h
|
||||
oiio_output_driver.cpp
|
||||
oiio_output_driver.h
|
||||
)
|
||||
|
||||
if(WITH_CYCLES_STANDALONE_GUI)
|
||||
list(APPEND SRC
|
||||
opengl/display_driver.cpp
|
||||
opengl/display_driver.h
|
||||
opengl/shader.cpp
|
||||
opengl/shader.h
|
||||
opengl/window.cpp
|
||||
opengl/window.h
|
||||
)
|
||||
endif()
|
||||
|
||||
add_executable(cycles ${SRC} ${INC} ${INC_SYS})
|
||||
unset(SRC)
|
||||
|
||||
target_link_libraries(cycles PRIVATE ${LIB})
|
||||
|
||||
if(APPLE)
|
||||
if(WITH_CYCLES_STANDALONE_GUI)
|
||||
# Frameworks used by SDL.
|
||||
string(CONCAT _cycles_sdl_frameworks
|
||||
" -framework AudioToolbox"
|
||||
" -framework AudioUnit"
|
||||
" -framework Cocoa"
|
||||
" -framework CoreAudio"
|
||||
" -framework CoreHaptics"
|
||||
" -framework CoreVideo"
|
||||
" -framework ForceFeedback"
|
||||
" -framework GameController"
|
||||
)
|
||||
set_property(
|
||||
TARGET cycles
|
||||
APPEND PROPERTY LINK_FLAGS
|
||||
"${_cycles_sdl_frameworks}"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CYCLES_STANDALONE_REPOSITORY)
|
||||
cycles_install_libraries(cycles)
|
||||
|
||||
if(WITH_USD AND USD_LIBRARY_DIR)
|
||||
install(DIRECTORY
|
||||
${USD_LIBRARY_DIR}/usd
|
||||
DESTINATION ${CMAKE_INSTALL_PREFIX}
|
||||
)
|
||||
|
||||
install(DIRECTORY
|
||||
${USD_LIBRARY_DIR}/../plugin/usd
|
||||
DESTINATION ${CMAKE_INSTALL_PREFIX}
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
install(
|
||||
TARGETS cycles
|
||||
DESTINATION ${app_install_dir})
|
||||
|
||||
if(DEFINED app_output_dir)
|
||||
set_target_properties(cycles
|
||||
PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${app_output_dir}")
|
||||
endif()
|
||||
|
||||
add_test(
|
||||
NAME cycles_version
|
||||
COMMAND $<TARGET_FILE:cycles> --version)
|
||||
endif()
|
||||
|
||||
if(WITH_CYCLES_PRECOMPUTE)
|
||||
set(SRC
|
||||
cycles_precompute.cpp
|
||||
)
|
||||
|
||||
add_executable(cycles_precompute ${SRC} ${INC} ${INC_SYS})
|
||||
unset(SRC)
|
||||
|
||||
target_link_libraries(cycles_precompute
|
||||
PRIVATE cycles_util
|
||||
PRIVATE bf::dependencies::openimageio
|
||||
PRIVATE bf::dependencies::optional::tbb
|
||||
PRIVATE bf::dependencies::optional::osl)
|
||||
|
||||
if(NOT CYCLES_STANDALONE_REPOSITORY)
|
||||
target_link_libraries(cycles_precompute
|
||||
PRIVATE bf::intern::guardedalloc)
|
||||
endif()
|
||||
|
||||
if(DEFINED app_output_dir)
|
||||
set_target_properties(cycles_precompute
|
||||
PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${app_output_dir}")
|
||||
endif()
|
||||
|
||||
install(
|
||||
TARGETS cycles_precompute
|
||||
DESTINATION ${app_install_dir})
|
||||
endif()
|
||||
296
blender-5.2.0/intern/cycles/app/cycles_precompute.cpp
Normal file
296
blender-5.2.0/intern/cycles/app/cycles_precompute.cpp
Normal file
@@ -0,0 +1,296 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "util/string.h"
|
||||
|
||||
#include "util/array.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/tbb.h"
|
||||
|
||||
#include "kernel/closure/bsdf_microfacet.h"
|
||||
#include "kernel/sample/sobol_burley.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
static float precompute_ggx_E(const float rough, const float mu, const float3 rand)
|
||||
{
|
||||
MicrofacetBsdf bsdf;
|
||||
bsdf.weight = one_float3();
|
||||
bsdf.sample_weight = 1.0f;
|
||||
bsdf.N = make_float3(0.0f, 0.0f, 1.0f);
|
||||
bsdf.alpha_x = bsdf.alpha_y = sqr(rough);
|
||||
bsdf.ior = 1.0f;
|
||||
bsdf.T = make_float3(1.0f, 0.0f, 0.0f);
|
||||
|
||||
bsdf_microfacet_ggx_setup(&bsdf);
|
||||
|
||||
float3 omega_in;
|
||||
Spectrum eval;
|
||||
float pdf = 0.0f;
|
||||
float sampled_eta;
|
||||
float2 sampled_roughness;
|
||||
bsdf_microfacet_ggx_sample(nullptr,
|
||||
(ShaderClosure *)&bsdf,
|
||||
make_float3(0.0f, 0.0f, 1.0f),
|
||||
make_float3(sqrtf(1.0f - sqr(mu)), 0.0f, mu),
|
||||
rand,
|
||||
&eval,
|
||||
&omega_in,
|
||||
&pdf,
|
||||
&sampled_roughness,
|
||||
&sampled_eta);
|
||||
if (pdf != 0.0f) {
|
||||
return average(eval) / pdf;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
static float precompute_ggx_glass_E(const float rough,
|
||||
const float mu,
|
||||
const float eta,
|
||||
const float3 rand)
|
||||
{
|
||||
MicrofacetBsdf bsdf;
|
||||
bsdf.weight = one_float3();
|
||||
bsdf.sample_weight = 1.0f;
|
||||
bsdf.N = make_float3(0.0f, 0.0f, 1.0f);
|
||||
bsdf.alpha_x = bsdf.alpha_y = sqr(rough);
|
||||
bsdf.ior = eta;
|
||||
bsdf.T = make_float3(1.0f, 0.0f, 0.0f);
|
||||
|
||||
bsdf_microfacet_ggx_glass_setup(&bsdf);
|
||||
|
||||
float3 omega_in;
|
||||
Spectrum eval;
|
||||
float pdf = 0.0f;
|
||||
float sampled_eta;
|
||||
float2 sampled_roughness;
|
||||
bsdf_microfacet_ggx_sample(nullptr,
|
||||
(ShaderClosure *)&bsdf,
|
||||
make_float3(0.0f, 0.0f, 1.0f),
|
||||
make_float3(sqrtf(1.0f - sqr(mu)), 0.0f, mu),
|
||||
rand,
|
||||
&eval,
|
||||
&omega_in,
|
||||
&pdf,
|
||||
&sampled_roughness,
|
||||
&sampled_eta);
|
||||
if (pdf != 0.0f) {
|
||||
return average(eval) / pdf;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
static float precompute_ggx_gen_schlick_s(
|
||||
const float rough, const float mu, const float eta, const float exponent, const float3 rand)
|
||||
{
|
||||
MicrofacetBsdf bsdf;
|
||||
bsdf.weight = one_float3();
|
||||
bsdf.sample_weight = 1.0f;
|
||||
bsdf.N = make_float3(0.0f, 0.0f, 1.0f);
|
||||
bsdf.alpha_x = bsdf.alpha_y = sqr(rough);
|
||||
bsdf.ior = eta;
|
||||
bsdf.T = make_float3(1.0f, 0.0f, 0.0f);
|
||||
|
||||
bsdf_microfacet_ggx_setup(&bsdf);
|
||||
|
||||
FresnelGeneralizedSchlick fresnel;
|
||||
fresnel.reflection_tint = one_float3();
|
||||
fresnel.transmission_tint = one_float3();
|
||||
fresnel.f0 = make_float3(0.0f, 1.0f, 0.0f);
|
||||
fresnel.f90 = make_float3(1.0f, 1.0f, 0.0f);
|
||||
fresnel.exponent = exponent;
|
||||
|
||||
bsdf.fresnel_type = MicrofacetFresnel::GENERALIZED_SCHLICK;
|
||||
bsdf.fresnel = &fresnel;
|
||||
|
||||
float3 omega_in;
|
||||
Spectrum eval;
|
||||
float pdf = 0.0f;
|
||||
float sampled_eta;
|
||||
float2 sampled_roughness;
|
||||
bsdf_microfacet_ggx_sample(nullptr,
|
||||
(ShaderClosure *)&bsdf,
|
||||
make_float3(0.0f, 0.0f, 1.0f),
|
||||
make_float3(sqrtf(1.0f - sqr(mu)), 0.0f, mu),
|
||||
rand,
|
||||
&eval,
|
||||
&omega_in,
|
||||
&pdf,
|
||||
&sampled_roughness,
|
||||
&sampled_eta);
|
||||
if (pdf != 0.0f) {
|
||||
/* The idea here is that the resulting Fresnel factor is always bounded by
|
||||
* F0..F90, so it's enough to precompute and store the interpolation factor. */
|
||||
return saturatef(eval.x / eval.y);
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
inline float ior_parametrization(const float z)
|
||||
{
|
||||
/* This parametrization ensures that the entire [1..inf] range of IORs is covered
|
||||
* and that most precision is allocated to the common areas (1-2). */
|
||||
return ior_from_F0(sqr(sqr(z)));
|
||||
}
|
||||
|
||||
struct PrecomputeTerm {
|
||||
int samples;
|
||||
int nx, ny, nz;
|
||||
std::function<float(float, float, float, float3)> evaluation;
|
||||
};
|
||||
|
||||
static bool cycles_precompute(std::string name)
|
||||
{
|
||||
std::map<string, PrecomputeTerm> precompute_terms;
|
||||
/* Overall albedo of the GGX microfacet BRDF, depending on cosI and roughness. */
|
||||
precompute_terms["ggx_E"] = {
|
||||
1 << 23, 32, 32, 1, [](const float rough, const float mu, float, const float3 rand) {
|
||||
return precompute_ggx_E(rough, mu, rand);
|
||||
}};
|
||||
/* Overall albedo of the GGX microfacet BRDF, averaged over cosI */
|
||||
precompute_terms["ggx_Eavg"] = {
|
||||
1 << 26, 32, 1, 1, [](const float rough, const float mu, float, const float3 rand) {
|
||||
return 2.0f * mu * precompute_ggx_E(rough, mu, rand);
|
||||
}};
|
||||
/* Overall albedo of the GGX microfacet BSDF with dielectric Fresnel,
|
||||
* depending on cosI and roughness, for IOR>1. */
|
||||
precompute_terms["ggx_glass_E"] = {
|
||||
1 << 23,
|
||||
16,
|
||||
16,
|
||||
16,
|
||||
[](const float rough, const float mu, const float z, const float3 rand) {
|
||||
const float ior = ior_parametrization(z);
|
||||
return precompute_ggx_glass_E(rough, mu, ior, rand);
|
||||
}};
|
||||
/* Overall albedo of the GGX microfacet BSDF with dielectric Fresnel,
|
||||
* averaged over cosI, for IOR>1. */
|
||||
precompute_terms["ggx_glass_Eavg"] = {
|
||||
1 << 26, 16, 1, 16, [](const float rough, const float mu, const float z, const float3 rand) {
|
||||
const float ior = ior_parametrization(z);
|
||||
return 2.0f * mu * precompute_ggx_glass_E(rough, mu, ior, rand);
|
||||
}};
|
||||
/* Overall albedo of the GGX microfacet BSDF with dielectric Fresnel,
|
||||
* depending on cosI and roughness, for IOR<1. */
|
||||
precompute_terms["ggx_glass_inv_E"] = {
|
||||
1 << 23,
|
||||
16,
|
||||
16,
|
||||
16,
|
||||
[](const float rough, const float mu, const float z, const float3 rand) {
|
||||
const float ior = ior_parametrization(z);
|
||||
return precompute_ggx_glass_E(rough, mu, 1.0f / ior, rand);
|
||||
}};
|
||||
/* Overall albedo of the GGX microfacet BSDF with dielectric Fresnel,
|
||||
* averaged over cosI, for IOR<1. */
|
||||
precompute_terms["ggx_glass_inv_Eavg"] = {
|
||||
1 << 26, 16, 1, 16, [](const float rough, const float mu, const float z, const float3 rand) {
|
||||
const float ior = ior_parametrization(z);
|
||||
return 2.0f * mu * precompute_ggx_glass_E(rough, mu, 1.0f / ior, rand);
|
||||
}};
|
||||
|
||||
/* Interpolation factor between F0 and F90 for the generalized Schlick Fresnel,
|
||||
* depending on cosI and roughness, for IOR>1, using dielectric Fresnel mode. */
|
||||
precompute_terms["ggx_gen_schlick_ior_s"] = {
|
||||
1 << 20,
|
||||
16,
|
||||
16,
|
||||
16,
|
||||
[](const float rough, const float mu, const float z, const float3 rand) {
|
||||
const float ior = ior_parametrization(z);
|
||||
return precompute_ggx_gen_schlick_s(rough, mu, ior, -1.0f, rand);
|
||||
}};
|
||||
|
||||
/* Interpolation factor between F0 and F90 for the generalized Schlick Fresnel,
|
||||
* depending on cosI and roughness, for IOR>1. */
|
||||
precompute_terms["ggx_gen_schlick_s"] = {
|
||||
1 << 20,
|
||||
16,
|
||||
16,
|
||||
16,
|
||||
[](const float rough, const float mu, const float z, const float3 rand) {
|
||||
/* Remap 0..1 to 0..inf, with 0.5 mapping to 5 (the default value). */
|
||||
const float exponent = 5.0f * ((1.0f - z) / z);
|
||||
return precompute_ggx_gen_schlick_s(rough, mu, 1.0f, exponent, rand);
|
||||
}};
|
||||
|
||||
if (!precompute_terms.contains(name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const PrecomputeTerm &term = precompute_terms[name];
|
||||
|
||||
const int samples = term.samples;
|
||||
const int nz = term.nz;
|
||||
const int ny = term.ny;
|
||||
const int nx = term.nx;
|
||||
|
||||
std::cout << "static const float table_" << name << "[" << nz * ny * nx << "] = {" << std::endl;
|
||||
for (int z = 0; z < nz; z++) {
|
||||
array<float> data(nx * ny);
|
||||
parallel_for(0, nx * ny, [&](int64_t i) {
|
||||
const int y = i / nx;
|
||||
const int x = i % nx;
|
||||
const uint seed = hash_uint2(x, y);
|
||||
double sum = 0.0;
|
||||
for (int sample = 0; sample < samples; sample++) {
|
||||
const float4 rand = sobol_burley_sample_4D(sample, 0, seed, 0xffffffff);
|
||||
|
||||
const float rough = (nx == 1) ? 0.0f : clamp(float(x) / float(nx - 1), 1e-4f, 1.0f);
|
||||
const float mu = (ny == 1) ? rand.w : clamp(float(y) / float(ny - 1), 1e-4f, 1.0f);
|
||||
const float ior = (nz == 1) ? 0.0f : clamp(float(z) / float(nz - 1), 1e-4f, 0.99f);
|
||||
|
||||
float value = term.evaluation(rough, mu, ior, make_float3(rand));
|
||||
if (isnan(value)) {
|
||||
value = 0.0f;
|
||||
}
|
||||
sum += (double)value;
|
||||
}
|
||||
data[y * nx + x] = saturatef(float(sum / double(samples)));
|
||||
});
|
||||
|
||||
/* Print data formatted as C++ array */
|
||||
for (int y = 0; y < ny; y++) {
|
||||
std::cout << " ";
|
||||
for (int x = 0; x < nx; x++) {
|
||||
std::cout << std::to_string(data[y * nx + x]);
|
||||
if (x + 1 < nx) {
|
||||
/* Next number will follow in same line */
|
||||
std::cout << "f, ";
|
||||
}
|
||||
else if (y + 1 < ny || z + 1 < nz) {
|
||||
/* Next number will follow in next line */
|
||||
std::cout << "f,";
|
||||
}
|
||||
else {
|
||||
/* No next number */
|
||||
std::cout << "f";
|
||||
}
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
/* If the array is three-dimensional, put an empty line between each slice. */
|
||||
if (ny > 1 && z + 1 < nz) {
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
std::cout << "};" << std::endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
int main(const int argc, const char **argv)
|
||||
{
|
||||
if (argc < 2) {
|
||||
return 1;
|
||||
}
|
||||
return ccl::cycles_precompute(argv[1]) ? 0 : 1;
|
||||
}
|
||||
573
blender-5.2.0/intern/cycles/app/cycles_standalone.cpp
Normal file
573
blender-5.2.0/intern/cycles/app/cycles_standalone.cpp
Normal file
@@ -0,0 +1,573 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "device/device.h"
|
||||
#include "scene/camera.h"
|
||||
#include "scene/integrator.h"
|
||||
#include "scene/scene.h"
|
||||
#include "session/buffers.h"
|
||||
#include "session/session.h"
|
||||
|
||||
#include "util/args.h"
|
||||
#include "util/log.h"
|
||||
#include "util/path.h"
|
||||
#include "util/progress.h"
|
||||
#include "util/string.h"
|
||||
#include "util/system.h"
|
||||
#ifdef WITH_CYCLES_STANDALONE_GUI
|
||||
# include "util/time.h"
|
||||
# include "util/transform.h"
|
||||
#endif
|
||||
#include "util/unique_ptr.h"
|
||||
#include "util/version.h"
|
||||
|
||||
#ifdef WITH_USD
|
||||
# include "hydra/file_reader.h"
|
||||
#endif
|
||||
|
||||
#include "app/cycles_xml.h"
|
||||
#include "app/oiio_output_driver.h"
|
||||
|
||||
#ifdef WITH_CYCLES_STANDALONE_GUI
|
||||
# include "opengl/display_driver.h"
|
||||
# include "opengl/window.h"
|
||||
#endif
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
struct Options {
|
||||
unique_ptr<Session> session;
|
||||
Scene *scene;
|
||||
string filepath;
|
||||
int width, height;
|
||||
SceneParams scene_params;
|
||||
SessionParams session_params;
|
||||
bool quiet;
|
||||
bool show_help, interactive, pause;
|
||||
string output_filepath;
|
||||
string output_pass;
|
||||
} options;
|
||||
|
||||
static void session_print(const string &str)
|
||||
{
|
||||
/* print with carriage return to overwrite previous */
|
||||
printf("\r%s", str.c_str());
|
||||
|
||||
/* add spaces to overwrite longer previous print */
|
||||
static int maxlen = 0;
|
||||
const int len = str.size();
|
||||
maxlen = max(len, maxlen);
|
||||
|
||||
for (int i = len; i < maxlen; i++) {
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
/* flush because we don't write an end of line */
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
static void session_print_status()
|
||||
{
|
||||
string status;
|
||||
string substatus;
|
||||
|
||||
/* get status */
|
||||
const double progress = options.session->progress.get_progress();
|
||||
options.session->progress.get_status(status, substatus);
|
||||
|
||||
if (!substatus.empty()) {
|
||||
status += ": " + substatus;
|
||||
}
|
||||
|
||||
/* print status */
|
||||
status = string_printf("Progress %05.2f %s", progress * 100, status.c_str());
|
||||
session_print(status);
|
||||
}
|
||||
|
||||
static BufferParams &session_buffer_params()
|
||||
{
|
||||
static BufferParams buffer_params;
|
||||
buffer_params.width = options.width;
|
||||
buffer_params.height = options.height;
|
||||
buffer_params.full_width = options.width;
|
||||
buffer_params.full_height = options.height;
|
||||
|
||||
return buffer_params;
|
||||
}
|
||||
|
||||
static void scene_init()
|
||||
{
|
||||
options.scene = options.session->scene.get();
|
||||
|
||||
/* Read XML or USD */
|
||||
#ifdef WITH_USD
|
||||
if (!string_endswith(string_to_lower(options.filepath), ".xml")) {
|
||||
HD_CYCLES_NS::HdCyclesFileReader::read(options.session.get(), options.filepath.c_str());
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
xml_read_file(options.scene, options.filepath.c_str());
|
||||
}
|
||||
|
||||
/* Camera width/height override? */
|
||||
if (!(options.width == 0 || options.height == 0)) {
|
||||
options.scene->camera->set_full_width(options.width);
|
||||
options.scene->camera->set_full_height(options.height);
|
||||
}
|
||||
else {
|
||||
options.width = options.scene->camera->get_full_width();
|
||||
options.height = options.scene->camera->get_full_height();
|
||||
}
|
||||
|
||||
/* Calculate Viewplane */
|
||||
options.scene->camera->compute_auto_viewplane();
|
||||
}
|
||||
|
||||
static void session_init()
|
||||
{
|
||||
options.output_pass = "combined";
|
||||
options.session = make_unique<Session>(options.session_params, options.scene_params);
|
||||
|
||||
#ifdef WITH_CYCLES_STANDALONE_GUI
|
||||
if (!options.session_params.background) {
|
||||
options.session->set_display_driver(make_unique<OpenGLDisplayDriver>(
|
||||
window_opengl_context_enable, window_opengl_context_disable));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!options.output_filepath.empty()) {
|
||||
options.session->set_output_driver(make_unique<OIIOOutputDriver>(
|
||||
options.output_filepath, options.output_pass, session_print));
|
||||
}
|
||||
|
||||
if (options.session_params.background && !options.quiet) {
|
||||
options.session->progress.set_update_callback([] { session_print_status(); });
|
||||
}
|
||||
#ifdef WITH_CYCLES_STANDALONE_GUI
|
||||
else {
|
||||
options.session->progress.set_update_callback([] { window_redraw(); });
|
||||
}
|
||||
#endif
|
||||
|
||||
/* load scene */
|
||||
scene_init();
|
||||
|
||||
/* add pass for output. */
|
||||
Pass *pass = options.scene->create_node<Pass>();
|
||||
pass->set_name(ustring(options.output_pass.c_str()));
|
||||
pass->set_type(PASS_COMBINED);
|
||||
|
||||
options.session->reset(options.session_params, session_buffer_params());
|
||||
options.session->start();
|
||||
}
|
||||
|
||||
static void session_exit()
|
||||
{
|
||||
if (options.session) {
|
||||
options.session.reset();
|
||||
}
|
||||
|
||||
if (options.session_params.background && !options.quiet) {
|
||||
session_print("Finished Rendering.");
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WITH_CYCLES_STANDALONE_GUI
|
||||
static void display_info(Progress &progress)
|
||||
{
|
||||
static double latency = 0.0;
|
||||
static double last = 0;
|
||||
const double elapsed = time_dt();
|
||||
string str;
|
||||
string interactive;
|
||||
|
||||
latency = (elapsed - last);
|
||||
last = elapsed;
|
||||
|
||||
double total_time;
|
||||
double sample_time;
|
||||
string status;
|
||||
string substatus;
|
||||
|
||||
progress.get_time(total_time, sample_time);
|
||||
progress.get_status(status, substatus);
|
||||
const double progress_val = progress.get_progress();
|
||||
|
||||
if (!substatus.empty()) {
|
||||
status += ": " + substatus;
|
||||
}
|
||||
|
||||
interactive = options.interactive ? "On" : "Off";
|
||||
|
||||
str = string_printf(
|
||||
"%s"
|
||||
" Time: %.2f"
|
||||
" Latency: %.4f"
|
||||
" Progress: %05.2f"
|
||||
" Average: %.4f"
|
||||
" Interactive: %s",
|
||||
status.c_str(),
|
||||
total_time,
|
||||
latency,
|
||||
progress_val * 100,
|
||||
sample_time,
|
||||
interactive.c_str());
|
||||
|
||||
window_display_info(str.c_str());
|
||||
|
||||
if (options.show_help) {
|
||||
window_display_help();
|
||||
}
|
||||
}
|
||||
|
||||
static void display()
|
||||
{
|
||||
options.session->draw();
|
||||
|
||||
display_info(options.session->progress);
|
||||
}
|
||||
|
||||
static void motion(const int x, const int y, int button)
|
||||
{
|
||||
if (options.interactive) {
|
||||
Transform matrix = options.session->scene->camera->get_matrix();
|
||||
|
||||
/* Translate */
|
||||
if (button == 0) {
|
||||
const float3 translate = make_float3(x * 0.01f, -(y * 0.01f), 0.0f);
|
||||
matrix = matrix * transform_translate(translate);
|
||||
}
|
||||
|
||||
/* Rotate */
|
||||
else if (button == 2) {
|
||||
const float4 r1 = make_float4((float)x * 0.1f, 0.0f, 1.0f, 0.0f);
|
||||
matrix = matrix * transform_rotate(DEG2RADF(r1.x), make_float3(r1.y, r1.z, r1.w));
|
||||
|
||||
const float4 r2 = make_float4(y * 0.1f, 1.0f, 0.0f, 0.0f);
|
||||
matrix = matrix * transform_rotate(DEG2RADF(r2.x), make_float3(r2.y, r2.z, r2.w));
|
||||
}
|
||||
|
||||
/* Update and Reset */
|
||||
options.session->scene->camera->set_matrix(matrix);
|
||||
options.session->scene->camera->need_flags_update = true;
|
||||
options.session->scene->camera->need_device_update = true;
|
||||
|
||||
options.session->reset(options.session_params, session_buffer_params());
|
||||
}
|
||||
}
|
||||
|
||||
static void resize(const int width, const int height)
|
||||
{
|
||||
options.width = width;
|
||||
options.height = height;
|
||||
|
||||
if (options.session) {
|
||||
/* Update camera */
|
||||
options.session->scene->camera->set_full_width(options.width);
|
||||
options.session->scene->camera->set_full_height(options.height);
|
||||
options.session->scene->camera->compute_auto_viewplane();
|
||||
options.session->scene->camera->need_flags_update = true;
|
||||
options.session->scene->camera->need_device_update = true;
|
||||
|
||||
options.session->reset(options.session_params, session_buffer_params());
|
||||
}
|
||||
}
|
||||
|
||||
static void keyboard(unsigned char key)
|
||||
{
|
||||
/* Toggle help */
|
||||
if (key == 'h') {
|
||||
options.show_help = !(options.show_help);
|
||||
|
||||
/* Reset */
|
||||
}
|
||||
else if (key == 'r') {
|
||||
options.session->reset(options.session_params, session_buffer_params());
|
||||
|
||||
/* Cancel */
|
||||
}
|
||||
else if (key == 27) { // escape
|
||||
options.session->progress.set_cancel("Canceled");
|
||||
|
||||
/* Pause */
|
||||
}
|
||||
else if (key == 'p') {
|
||||
options.pause = !options.pause;
|
||||
options.session->set_pause(options.pause);
|
||||
}
|
||||
|
||||
/* Interactive Mode */
|
||||
else if (key == 'i') {
|
||||
options.interactive = !(options.interactive);
|
||||
|
||||
/* Navigation */
|
||||
}
|
||||
else if (options.interactive && (key == 'w' || key == 'a' || key == 's' || key == 'd')) {
|
||||
Transform matrix = options.session->scene->camera->get_matrix();
|
||||
float3 translate;
|
||||
|
||||
if (key == 'w') {
|
||||
translate = make_float3(0.0f, 0.0f, 0.1f);
|
||||
}
|
||||
else if (key == 's') {
|
||||
translate = make_float3(0.0f, 0.0f, -0.1f);
|
||||
}
|
||||
else if (key == 'a') {
|
||||
translate = make_float3(-0.1f, 0.0f, 0.0f);
|
||||
}
|
||||
else if (key == 'd') {
|
||||
translate = make_float3(0.1f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
matrix = matrix * transform_translate(translate);
|
||||
|
||||
/* Update and Reset */
|
||||
options.session->scene->camera->set_matrix(matrix);
|
||||
options.session->scene->camera->need_flags_update = true;
|
||||
options.session->scene->camera->need_device_update = true;
|
||||
|
||||
options.session->reset(options.session_params, session_buffer_params());
|
||||
}
|
||||
|
||||
/* Set Max Bounces */
|
||||
else if (options.interactive && (key == '0' || key == '1' || key == '2' || key == '3')) {
|
||||
int bounce;
|
||||
switch (key) {
|
||||
case '0':
|
||||
bounce = 0;
|
||||
break;
|
||||
case '1':
|
||||
bounce = 1;
|
||||
break;
|
||||
case '2':
|
||||
bounce = 2;
|
||||
break;
|
||||
case '3':
|
||||
bounce = 3;
|
||||
break;
|
||||
default:
|
||||
bounce = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
options.session->scene->integrator->set_max_bounce(bounce);
|
||||
|
||||
options.session->reset(options.session_params, session_buffer_params());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void parse_int(OIIO::cspan<const char *> argv, int *i)
|
||||
{
|
||||
assert(argv.size() == 2);
|
||||
*i = atoi(argv[1]);
|
||||
}
|
||||
|
||||
static void parse_string(OIIO::cspan<const char *> argv, std::string *s)
|
||||
{
|
||||
assert(argv.size() == 2);
|
||||
*s = argv[1];
|
||||
}
|
||||
|
||||
static void options_parse(const int argc, const char **argv)
|
||||
{
|
||||
options.width = 1024;
|
||||
options.height = 512;
|
||||
options.filepath = "";
|
||||
options.session = nullptr;
|
||||
options.quiet = false;
|
||||
options.session_params.use_auto_tile = false;
|
||||
options.session_params.tile_size = 0;
|
||||
|
||||
/* device names */
|
||||
string device_names;
|
||||
string devicename = "CPU";
|
||||
bool list = false;
|
||||
|
||||
/* List devices for which support is compiled in. */
|
||||
const vector<DeviceType> types = Device::available_types();
|
||||
for (const DeviceType type : types) {
|
||||
if (!device_names.empty()) {
|
||||
device_names += ", ";
|
||||
}
|
||||
|
||||
device_names += Device::string_from_type(type);
|
||||
}
|
||||
|
||||
/* shading system */
|
||||
string ssname = "svm";
|
||||
|
||||
/* parse options */
|
||||
ArgParse ap;
|
||||
bool help = false;
|
||||
bool profile = false;
|
||||
bool version = false;
|
||||
string log_level;
|
||||
|
||||
ap.usage("cycles [options] file.xml");
|
||||
ap.arg("filename").hidden().action([&](auto argv) { options.filepath = argv[0]; });
|
||||
ap.arg("--device %s:DEVICE").help("Devices to use: " + device_names).action([&](auto argv) {
|
||||
parse_string(argv, &devicename);
|
||||
});
|
||||
#ifdef WITH_OSL
|
||||
ap.arg("--shadingsys %s:SHADINGSYSTEM")
|
||||
.help("Shading system to use: svm, osl")
|
||||
.action([&](auto argv) { parse_string(argv, &ssname); });
|
||||
#endif
|
||||
ap.arg("--background", &options.session_params.background)
|
||||
.help("Render in background, without user interface");
|
||||
ap.arg("--quiet", &options.quiet).help("In background mode, don't print progress messages");
|
||||
ap.arg("--samples %d:SAMPLES").help("Number of samples to render").action([&](auto argv) {
|
||||
parse_int(argv, &options.session_params.samples);
|
||||
});
|
||||
ap.arg("--output %s:OUTPUT").help("File path to write output image").action([&](auto argv) {
|
||||
parse_string(argv, &options.output_filepath);
|
||||
});
|
||||
ap.arg("--threads %d:THREADS").help("CPU Rendering Threads").action([&](auto argv) {
|
||||
parse_int(argv, &options.session_params.threads);
|
||||
});
|
||||
ap.arg("--width %d:WIDTH").help("Image width in pixelx").action([&](auto argv) {
|
||||
parse_int(argv, &options.width);
|
||||
});
|
||||
ap.arg("--height %d:HEIGHT").help("Image height in pixel").action([&](auto argv) {
|
||||
parse_int(argv, &options.height);
|
||||
});
|
||||
ap.arg("--tile-size %d:TILE_SIZE").help("Tile size in pixels").action([&](auto argv) {
|
||||
parse_int(argv, &options.session_params.tile_size);
|
||||
});
|
||||
ap.arg("--list-devices", &list).help("List information about all available devices");
|
||||
ap.arg("--profile", &profile).help("Enable profile logging");
|
||||
ap.arg("--log-level %s:LEVEL")
|
||||
.help("Log verbosity: fatal, error, warning, info, stats, debug")
|
||||
.action([&](auto argv) { parse_string(argv, &log_level); });
|
||||
ap.arg("--help", &help).help("Print help message");
|
||||
ap.arg("--version", &version).help("Print version number");
|
||||
|
||||
if (ap.parse_args(argc, argv) < 0) {
|
||||
fprintf(stderr, "%s\n", ap.geterror().c_str());
|
||||
ap.print_help();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!log_level.empty()) {
|
||||
log_level_set(log_level);
|
||||
}
|
||||
|
||||
if (list) {
|
||||
const vector<DeviceInfo> devices = Device::available_devices();
|
||||
printf("Devices:\n");
|
||||
|
||||
for (const DeviceInfo &info : devices) {
|
||||
printf(" %-10s%s%s\n",
|
||||
Device::string_from_type(info.type).c_str(),
|
||||
info.description.c_str(),
|
||||
(info.display_device) ? " (display)" : "");
|
||||
}
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
else if (version) {
|
||||
printf("%s\n", CYCLES_VERSION_STRING);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
else if (help || options.filepath.empty()) {
|
||||
ap.print_help();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
options.session_params.use_profiling = profile;
|
||||
|
||||
if (ssname == "osl") {
|
||||
options.scene_params.shadingsystem = SHADINGSYSTEM_OSL;
|
||||
}
|
||||
else if (ssname == "svm") {
|
||||
options.scene_params.shadingsystem = SHADINGSYSTEM_SVM;
|
||||
}
|
||||
|
||||
#ifndef WITH_CYCLES_STANDALONE_GUI
|
||||
options.session_params.background = true;
|
||||
#endif
|
||||
|
||||
if (options.session_params.tile_size > 0) {
|
||||
options.session_params.use_auto_tile = true;
|
||||
}
|
||||
|
||||
/* find matching device */
|
||||
const DeviceType device_type = Device::type_from_string(devicename.c_str());
|
||||
vector<DeviceInfo> devices = Device::available_devices(DEVICE_MASK(device_type));
|
||||
|
||||
bool device_available = false;
|
||||
if (!devices.empty()) {
|
||||
options.session_params.device = devices.front();
|
||||
device_available = true;
|
||||
}
|
||||
|
||||
/* handle invalid configurations */
|
||||
if (options.session_params.device.type == DEVICE_NONE || !device_available) {
|
||||
fprintf(stderr, "Unknown device: %s\n", devicename.c_str());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#ifdef WITH_OSL
|
||||
else if (!(ssname == "osl" || ssname == "svm")) {
|
||||
fprintf(stderr, "Unknown shading system: %s\n", ssname.c_str());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
else if (options.scene_params.shadingsystem == SHADINGSYSTEM_OSL &&
|
||||
options.session_params.device.type != DEVICE_CPU)
|
||||
{
|
||||
fprintf(stderr, "OSL shading system only works with CPU device\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#endif
|
||||
else if (options.session_params.samples < 0) {
|
||||
fprintf(stderr, "Invalid number of samples: %d\n", options.session_params.samples);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
else if (options.filepath.empty()) {
|
||||
fprintf(stderr, "No file path specified\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
using namespace ccl;
|
||||
|
||||
int main(const int argc, const char **argv)
|
||||
{
|
||||
log_init(nullptr);
|
||||
path_init();
|
||||
system_max_open_files_ensure();
|
||||
options_parse(argc, argv);
|
||||
|
||||
#ifdef WITH_CYCLES_STANDALONE_GUI
|
||||
if (options.session_params.background) {
|
||||
#endif
|
||||
session_init();
|
||||
options.session->wait();
|
||||
session_exit();
|
||||
#ifdef WITH_CYCLES_STANDALONE_GUI
|
||||
}
|
||||
else {
|
||||
const string title = "Cycles: " + path_filename(options.filepath);
|
||||
|
||||
/* init/exit are callback so they run while GL is initialized */
|
||||
window_main_loop(title.c_str(),
|
||||
options.width,
|
||||
options.height,
|
||||
session_init,
|
||||
session_exit,
|
||||
resize,
|
||||
display,
|
||||
keyboard,
|
||||
motion);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
884
blender-5.2.0/intern/cycles/app/cycles_xml.cpp
Normal file
884
blender-5.2.0/intern/cycles/app/cycles_xml.cpp
Normal file
@@ -0,0 +1,884 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
|
||||
#include "graph/node_xml.h"
|
||||
|
||||
#include "scene/background.h"
|
||||
#include "scene/camera.h"
|
||||
#include "scene/film.h"
|
||||
#include "scene/integrator.h"
|
||||
#include "scene/light.h"
|
||||
#include "scene/mesh.h"
|
||||
#include "scene/object.h"
|
||||
#include "scene/osl.h"
|
||||
#include "scene/scene.h"
|
||||
#include "scene/shader.h"
|
||||
#include "scene/shader_graph.h"
|
||||
#include "scene/shader_nodes.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/path.h"
|
||||
#include "util/projection.h"
|
||||
#include "util/string.h"
|
||||
#include "util/transform.h"
|
||||
#include "util/xml.h"
|
||||
|
||||
#include "app/cycles_xml.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* XML reading state */
|
||||
|
||||
struct XMLReadState : public XMLReader {
|
||||
Scene *scene = nullptr; /* Scene pointer. */
|
||||
Transform tfm; /* Current transform state. */
|
||||
bool smooth = false; /* Smooth normal state. */
|
||||
Shader *shader = nullptr; /* Current shader. */
|
||||
string base; /* Base path to current file. */
|
||||
float dicing_rate = 1.0f; /* Current dicing rate. */
|
||||
Object *object = nullptr; /* Current object. */
|
||||
|
||||
XMLReadState()
|
||||
{
|
||||
tfm = transform_identity();
|
||||
}
|
||||
};
|
||||
|
||||
/* Attribute Reading */
|
||||
|
||||
static bool xml_read_int(int *value, const xml_node node, const char *name)
|
||||
{
|
||||
const xml_attribute attr = node.attribute(name);
|
||||
|
||||
if (attr) {
|
||||
*value = atoi(attr.value());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool xml_read_int_array(vector<int> &value, const xml_node node, const char *name)
|
||||
{
|
||||
const xml_attribute attr = node.attribute(name);
|
||||
|
||||
if (attr) {
|
||||
vector<string> tokens;
|
||||
string_split(tokens, attr.value());
|
||||
|
||||
for (const string &token : tokens) {
|
||||
value.push_back(atoi(token.c_str()));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool xml_read_float(float *value, const xml_node node, const char *name)
|
||||
{
|
||||
const xml_attribute attr = node.attribute(name);
|
||||
|
||||
if (attr) {
|
||||
*value = (float)atof(attr.value());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool xml_read_float_array(vector<float> &value, const xml_node node, const char *name)
|
||||
{
|
||||
const xml_attribute attr = node.attribute(name);
|
||||
|
||||
if (attr) {
|
||||
vector<string> tokens;
|
||||
string_split(tokens, attr.value());
|
||||
|
||||
for (const string &token : tokens) {
|
||||
value.push_back((float)atof(token.c_str()));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool xml_read_float3(float3 *value, const xml_node node, const char *name)
|
||||
{
|
||||
vector<float> array;
|
||||
|
||||
if (xml_read_float_array(array, node, name) && array.size() == 3) {
|
||||
*value = make_float3(array[0], array[1], array[2]);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool xml_read_float3_array(vector<packed_float3> &value,
|
||||
const xml_node node,
|
||||
const char *name)
|
||||
{
|
||||
vector<float> array;
|
||||
|
||||
if (xml_read_float_array(array, node, name)) {
|
||||
for (size_t i = 0; i < array.size(); i += 3) {
|
||||
value.push_back(make_float3(array[i + 0], array[i + 1], array[i + 2]));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool xml_read_float4(float4 *value, const xml_node node, const char *name)
|
||||
{
|
||||
vector<float> array;
|
||||
|
||||
if (xml_read_float_array(array, node, name) && array.size() == 4) {
|
||||
*value = make_float4(array[0], array[1], array[2], array[3]);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool xml_read_string(string *str, const xml_node node, const char *name)
|
||||
{
|
||||
const xml_attribute attr = node.attribute(name);
|
||||
|
||||
if (attr) {
|
||||
*str = attr.value();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool xml_equal_string(const xml_node node, const char *name, const char *value)
|
||||
{
|
||||
const xml_attribute attr = node.attribute(name);
|
||||
|
||||
if (attr) {
|
||||
return string_iequals(attr.value(), value);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Camera */
|
||||
|
||||
static void xml_read_camera(XMLReadState &state, const xml_node node)
|
||||
{
|
||||
Camera *cam = state.scene->camera;
|
||||
|
||||
int width = -1;
|
||||
int height = -1;
|
||||
xml_read_int(&width, node, "width");
|
||||
xml_read_int(&height, node, "height");
|
||||
|
||||
cam->set_full_width(width);
|
||||
cam->set_full_height(height);
|
||||
|
||||
xml_read_node(state, cam, node);
|
||||
|
||||
cam->set_matrix(state.tfm);
|
||||
|
||||
cam->need_flags_update = true;
|
||||
cam->update(state.scene);
|
||||
}
|
||||
|
||||
/* Shader */
|
||||
|
||||
static void xml_read_shader_graph(XMLReadState &state, Shader *shader, const xml_node graph_node)
|
||||
{
|
||||
xml_read_node(state, shader, graph_node);
|
||||
|
||||
unique_ptr<ShaderGraph> graph = make_unique<ShaderGraph>();
|
||||
|
||||
/* local state, shader nodes can't link to nodes outside the shader graph */
|
||||
XMLReader graph_reader;
|
||||
graph_reader.node_map[ustring("output")] = graph->output();
|
||||
|
||||
for (xml_node node = graph_node.first_child(); node; node = node.next_sibling()) {
|
||||
ustring node_name(node.name());
|
||||
|
||||
if (node_name == "connect") {
|
||||
/* connect nodes */
|
||||
vector<string> from_tokens;
|
||||
vector<string> to_tokens;
|
||||
|
||||
string_split(from_tokens, node.attribute("from").value());
|
||||
string_split(to_tokens, node.attribute("to").value());
|
||||
|
||||
if (from_tokens.size() == 2 && to_tokens.size() == 2) {
|
||||
const ustring from_node_name(from_tokens[0]);
|
||||
const ustring from_socket_name(from_tokens[1]);
|
||||
const ustring to_node_name(to_tokens[0]);
|
||||
const ustring to_socket_name(to_tokens[1]);
|
||||
|
||||
/* find nodes and sockets */
|
||||
ShaderOutput *output = nullptr;
|
||||
ShaderInput *input = nullptr;
|
||||
|
||||
if (graph_reader.node_map.contains(from_node_name)) {
|
||||
ShaderNode *fromnode = (ShaderNode *)graph_reader.node_map[from_node_name];
|
||||
|
||||
for (ShaderOutput *out : fromnode->outputs) {
|
||||
if (string_iequals(out->socket_type.name.string(), from_socket_name.string())) {
|
||||
output = out;
|
||||
}
|
||||
}
|
||||
|
||||
if (!output) {
|
||||
LOG_ERROR << "Unknown output socket name \"" << from_node_name << "\" on \""
|
||||
<< from_socket_name << "\".";
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG_ERROR << "Unknown shader node name \"" << from_node_name << "\"";
|
||||
}
|
||||
|
||||
if (graph_reader.node_map.contains(to_node_name)) {
|
||||
ShaderNode *tonode = (ShaderNode *)graph_reader.node_map[to_node_name];
|
||||
|
||||
for (ShaderInput *in : tonode->inputs) {
|
||||
if (string_iequals(in->socket_type.name.string(), to_socket_name.string())) {
|
||||
input = in;
|
||||
}
|
||||
}
|
||||
|
||||
if (!input) {
|
||||
LOG_ERROR << "Unknown input socket name \"" << to_socket_name << "\" on \""
|
||||
<< to_node_name << "\"";
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG_ERROR << "Unknown shader node name \"" << to_node_name << "\"";
|
||||
}
|
||||
|
||||
/* connect */
|
||||
if (output && input) {
|
||||
graph->connect(output, input);
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG_ERROR << "Invalid from or to value for connect node.";
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
ShaderNode *snode = nullptr;
|
||||
|
||||
#ifdef WITH_OSL
|
||||
if (node_name == "osl_shader") {
|
||||
ShaderManager *manager = state.scene->shader_manager.get();
|
||||
|
||||
if (manager->use_osl()) {
|
||||
std::string filepath;
|
||||
|
||||
if (xml_read_string(&filepath, node, "src")) {
|
||||
if (path_is_relative(filepath)) {
|
||||
filepath = path_join(state.base, filepath);
|
||||
}
|
||||
|
||||
snode = OSLShaderManager::osl_node(graph.get(), state.scene, filepath, "");
|
||||
|
||||
if (!snode) {
|
||||
LOG_ERROR << "Failed to create OSL node from \"" << filepath << "\"";
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG_ERROR << "OSL node missing \"src\" attribute.";
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG_ERROR << "OSL node without using --shadingsys osl.";
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
/* exception for name collision */
|
||||
if (node_name == "background") {
|
||||
node_name = "background_shader";
|
||||
}
|
||||
|
||||
const NodeType *node_type = NodeType::find(node_name);
|
||||
|
||||
if (!node_type) {
|
||||
LOG_ERROR << "Unknown shader node \"" << node.name() << "\"";
|
||||
continue;
|
||||
}
|
||||
if (node_type->type != NodeType::SHADER) {
|
||||
LOG_ERROR << "Node type \"" << node_type->name << "\" is not a shader node";
|
||||
continue;
|
||||
}
|
||||
if (node_type->create == nullptr) {
|
||||
LOG_ERROR << "Can't create abstract node type \""
|
||||
<< "\"";
|
||||
continue;
|
||||
}
|
||||
|
||||
snode = graph->create_node(node_type);
|
||||
}
|
||||
|
||||
xml_read_node(graph_reader, snode, node);
|
||||
|
||||
if (node_name == "image_texture") {
|
||||
ImageTextureNode *img = (ImageTextureNode *)snode;
|
||||
const ustring filename(path_join(state.base, img->get_filename().string()));
|
||||
img->set_filename(filename);
|
||||
}
|
||||
else if (node_name == "environment_texture") {
|
||||
EnvironmentTextureNode *env = (EnvironmentTextureNode *)snode;
|
||||
const ustring filename(path_join(state.base, env->get_filename().string()));
|
||||
env->set_filename(filename);
|
||||
}
|
||||
}
|
||||
|
||||
shader->set_graph(std::move(graph));
|
||||
shader->tag_update(state.scene);
|
||||
}
|
||||
|
||||
static void xml_read_shader(XMLReadState &state, const xml_node node)
|
||||
{
|
||||
Shader *shader = state.scene->create_node<Shader>();
|
||||
xml_read_shader_graph(state, shader, node);
|
||||
}
|
||||
|
||||
/* Background */
|
||||
|
||||
static void xml_read_background(XMLReadState &state, const xml_node node)
|
||||
{
|
||||
/* Background Settings */
|
||||
xml_read_node(state, state.scene->background, node);
|
||||
|
||||
/* Background Shader */
|
||||
Shader *shader = state.scene->default_background;
|
||||
xml_read_shader_graph(state, shader, node);
|
||||
}
|
||||
|
||||
/* Mesh */
|
||||
|
||||
static Mesh *xml_add_mesh(Scene *scene, const Transform &tfm, Object *object)
|
||||
{
|
||||
if (object && object->get_geometry()->is_mesh()) {
|
||||
/* Use existing object and mesh */
|
||||
object->set_tfm(tfm);
|
||||
Geometry *geometry = object->get_geometry();
|
||||
return static_cast<Mesh *>(geometry);
|
||||
}
|
||||
|
||||
/* Create mesh */
|
||||
Mesh *mesh = scene->create_node<Mesh>();
|
||||
|
||||
/* Create object. */
|
||||
object = scene->create_node<Object>();
|
||||
object->set_geometry(mesh);
|
||||
object->set_tfm(tfm);
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
static void xml_read_mesh(const XMLReadState &state, const xml_node node)
|
||||
{
|
||||
/* add mesh */
|
||||
Mesh *mesh = xml_add_mesh(state.scene, state.tfm, state.object);
|
||||
array<Node *> used_shaders = mesh->get_used_shaders();
|
||||
used_shaders.push_back_slow(state.shader);
|
||||
mesh->set_used_shaders(used_shaders);
|
||||
|
||||
/* read state */
|
||||
const int shader = 0;
|
||||
const bool smooth = state.smooth;
|
||||
|
||||
/* read vertices and polygons */
|
||||
vector<packed_float3> P;
|
||||
vector<packed_float3> VN; /* Vertex normals */
|
||||
vector<float> UV;
|
||||
vector<float> T; /* UV tangents */
|
||||
vector<float> TS; /* UV tangent signs */
|
||||
vector<int> verts;
|
||||
vector<int> nverts;
|
||||
|
||||
xml_read_float3_array(P, node, "P");
|
||||
xml_read_int_array(verts, node, "verts");
|
||||
xml_read_int_array(nverts, node, "nverts");
|
||||
|
||||
if (xml_equal_string(node, "subdivision", "catmull-clark")) {
|
||||
mesh->set_subdivision_type(Mesh::SUBDIVISION_CATMULL_CLARK);
|
||||
}
|
||||
else if (xml_equal_string(node, "subdivision", "linear")) {
|
||||
mesh->set_subdivision_type(Mesh::SUBDIVISION_LINEAR);
|
||||
}
|
||||
|
||||
if (mesh->get_subdivision_type() == Mesh::SUBDIVISION_NONE) {
|
||||
/* create vertices */
|
||||
|
||||
size_t num_triangles = 0;
|
||||
for (size_t i = 0; i < nverts.size(); i++) {
|
||||
num_triangles += nverts[i] - 2;
|
||||
}
|
||||
mesh->resize_mesh(P.size(), num_triangles);
|
||||
std::copy_n(P.data(), P.size(), mesh->get_position_for_write());
|
||||
|
||||
int *triangles = mesh->get_triangles().data();
|
||||
|
||||
/* create triangles */
|
||||
int tri_index = 0;
|
||||
int index_offset = 0;
|
||||
|
||||
for (size_t i = 0; i < nverts.size(); i++) {
|
||||
for (int j = 0; j < nverts[i] - 2; j++) {
|
||||
const int v0 = verts[index_offset];
|
||||
const int v1 = verts[index_offset + j + 1];
|
||||
const int v2 = verts[index_offset + j + 2];
|
||||
|
||||
assert(v0 < (int)P.size());
|
||||
assert(v1 < (int)P.size());
|
||||
assert(v2 < (int)P.size());
|
||||
|
||||
triangles[tri_index * 3 + 0] = v0;
|
||||
triangles[tri_index * 3 + 1] = v1;
|
||||
triangles[tri_index * 3 + 2] = v2;
|
||||
tri_index++;
|
||||
}
|
||||
|
||||
index_offset += nverts[i];
|
||||
}
|
||||
std::ranges::fill(mesh->get_smooth(), smooth);
|
||||
std::ranges::fill(mesh->get_shader(), shader);
|
||||
|
||||
mesh->tag_triangles_modified();
|
||||
mesh->tag_shader_modified();
|
||||
mesh->tag_smooth_modified();
|
||||
|
||||
/* Vertex normals */
|
||||
if (xml_read_float3_array(VN, node, Attribute::standard_name(ATTR_STD_VERTEX_NORMAL))) {
|
||||
Attribute *attr = mesh->attributes.add(ATTR_STD_VERTEX_NORMAL);
|
||||
packed_normal *fdata = attr->data_for_write<packed_normal>();
|
||||
|
||||
/* Loop over the normals */
|
||||
for (auto n : VN) {
|
||||
fdata[0] = packed_normal(n);
|
||||
fdata++;
|
||||
}
|
||||
}
|
||||
|
||||
/* UV map */
|
||||
if (xml_read_float_array(UV, node, "UV") ||
|
||||
xml_read_float_array(UV, node, Attribute::standard_name(ATTR_STD_UV)))
|
||||
{
|
||||
Attribute *attr = mesh->attributes.add(ATTR_STD_UV);
|
||||
float2 *fdata = attr->data_for_write<float2>();
|
||||
|
||||
/* Loop over the triangles */
|
||||
index_offset = 0;
|
||||
for (size_t i = 0; i < nverts.size(); i++) {
|
||||
for (int j = 0; j < nverts[i] - 2; j++) {
|
||||
const int v0 = index_offset;
|
||||
const int v1 = index_offset + j + 1;
|
||||
const int v2 = index_offset + j + 2;
|
||||
|
||||
assert(v0 * 2 + 1 < (int)UV.size());
|
||||
assert(v1 * 2 + 1 < (int)UV.size());
|
||||
assert(v2 * 2 + 1 < (int)UV.size());
|
||||
|
||||
fdata[0] = make_float2(UV[v0 * 2], UV[v0 * 2 + 1]);
|
||||
fdata[1] = make_float2(UV[v1 * 2], UV[v1 * 2 + 1]);
|
||||
fdata[2] = make_float2(UV[v2 * 2], UV[v2 * 2 + 1]);
|
||||
fdata += 3;
|
||||
}
|
||||
|
||||
index_offset += nverts[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* Tangents */
|
||||
if (xml_read_float_array(T, node, Attribute::standard_name(ATTR_STD_UV_TANGENT))) {
|
||||
Attribute *attr = mesh->attributes.add(ATTR_STD_UV_TANGENT);
|
||||
packed_float3 *fdata = attr->data_for_write<packed_float3>();
|
||||
|
||||
/* Loop over the triangles */
|
||||
index_offset = 0;
|
||||
for (size_t i = 0; i < nverts.size(); i++) {
|
||||
for (int j = 0; j < nverts[i] - 2; j++) {
|
||||
const int v0 = index_offset;
|
||||
const int v1 = index_offset + j + 1;
|
||||
const int v2 = index_offset + j + 2;
|
||||
|
||||
assert(v0 * 3 + 2 < (int)T.size());
|
||||
assert(v1 * 3 + 2 < (int)T.size());
|
||||
assert(v2 * 3 + 2 < (int)T.size());
|
||||
|
||||
fdata[0] = make_float3(T[v0 * 3], T[v0 * 3 + 1], T[v0 * 3 + 2]);
|
||||
fdata[1] = make_float3(T[v1 * 3], T[v1 * 3 + 1], T[v1 * 3 + 2]);
|
||||
fdata[2] = make_float3(T[v2 * 3], T[v2 * 3 + 1], T[v2 * 3 + 2]);
|
||||
fdata += 3;
|
||||
}
|
||||
index_offset += nverts[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* Tangent signs */
|
||||
if (xml_read_float_array(TS, node, Attribute::standard_name(ATTR_STD_UV_TANGENT_SIGN))) {
|
||||
Attribute *attr = mesh->attributes.add(ATTR_STD_UV_TANGENT_SIGN);
|
||||
float *fdata = attr->data_for_write<float>();
|
||||
|
||||
/* Loop over the triangles */
|
||||
index_offset = 0;
|
||||
for (size_t i = 0; i < nverts.size(); i++) {
|
||||
for (int j = 0; j < nverts[i] - 2; j++) {
|
||||
const int v0 = index_offset;
|
||||
const int v1 = index_offset + j + 1;
|
||||
const int v2 = index_offset + j + 2;
|
||||
|
||||
assert(v0 < (int)TS.size());
|
||||
assert(v1 < (int)TS.size());
|
||||
assert(v2 < (int)TS.size());
|
||||
|
||||
fdata[0] = TS[v0];
|
||||
fdata[1] = TS[v1];
|
||||
fdata[2] = TS[v2];
|
||||
fdata += 3;
|
||||
}
|
||||
index_offset += nverts[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* create vertices */
|
||||
mesh->resize_mesh(P.size(), 0);
|
||||
|
||||
size_t num_corners = 0;
|
||||
for (size_t i = 0; i < nverts.size(); i++) {
|
||||
num_corners += nverts[i];
|
||||
}
|
||||
mesh->resize_subd_faces(nverts.size(), num_corners);
|
||||
|
||||
Attribute *subd_attr_P = mesh->subd_attributes.add(ATTR_STD_POSITION);
|
||||
subd_attr_P->resize(P.size());
|
||||
std::copy_n(P.data(), P.size(), subd_attr_P->data_for_write<packed_float3>());
|
||||
|
||||
int *subd_start_corner = mesh->get_subd_start_corner().data();
|
||||
int *subd_num_corners = mesh->get_subd_num_corners().data();
|
||||
int *subd_ptex_offset = mesh->get_subd_ptex_offset().data();
|
||||
int *subd_face_corners = mesh->get_subd_face_corners().data();
|
||||
|
||||
std::ranges::fill(mesh->get_subd_shader(), shader);
|
||||
std::ranges::fill(mesh->get_subd_smooth(), smooth);
|
||||
|
||||
std::ranges::copy(verts, subd_face_corners);
|
||||
|
||||
/* create subd_faces */
|
||||
int corner_index = 0;
|
||||
int ptex_offset = 0;
|
||||
|
||||
for (size_t i = 0; i < nverts.size(); i++) {
|
||||
subd_start_corner[i] = corner_index;
|
||||
subd_num_corners[i] = nverts[i];
|
||||
corner_index += nverts[i];
|
||||
|
||||
subd_ptex_offset[i] = ptex_offset;
|
||||
const int num_ptex = (nverts[i] == 4) ? 1 : nverts[i];
|
||||
ptex_offset += num_ptex;
|
||||
}
|
||||
|
||||
mesh->tag_subd_face_corners_modified();
|
||||
mesh->tag_subd_start_corner_modified();
|
||||
mesh->tag_subd_num_corners_modified();
|
||||
mesh->tag_subd_shader_modified();
|
||||
mesh->tag_subd_smooth_modified();
|
||||
mesh->tag_subd_ptex_offset_modified();
|
||||
|
||||
/* UV map */
|
||||
if (xml_read_float_array(UV, node, "UV") ||
|
||||
xml_read_float_array(UV, node, Attribute::standard_name(ATTR_STD_UV)))
|
||||
{
|
||||
Attribute *attr = mesh->subd_attributes.add(ATTR_STD_UV);
|
||||
packed_float3 *fdata = attr->data_for_write<packed_float3>();
|
||||
|
||||
int index_offset = 0;
|
||||
for (size_t i = 0; i < nverts.size(); i++) {
|
||||
for (int j = 0; j < nverts[i]; j++) {
|
||||
*(fdata++) = make_float3(UV[index_offset++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* setup subd params */
|
||||
float dicing_rate = state.dicing_rate;
|
||||
xml_read_float(&dicing_rate, node, "dicing_rate");
|
||||
dicing_rate = std::max(0.1f, dicing_rate);
|
||||
|
||||
mesh->set_subd_dicing_rate(dicing_rate);
|
||||
mesh->set_subd_objecttoworld(state.tfm);
|
||||
}
|
||||
|
||||
/* we don't yet support arbitrary attributes, for now add vertex
|
||||
* coordinates as generated coordinates if requested */
|
||||
if (mesh->need_attribute(state.scene, ATTR_STD_GENERATED)) {
|
||||
Attribute *attr = mesh->attributes.add(ATTR_STD_GENERATED);
|
||||
std::copy_n(mesh->get_position(), mesh->num_verts(), attr->data_for_write<packed_float3>());
|
||||
}
|
||||
}
|
||||
|
||||
/* Light */
|
||||
|
||||
static void xml_read_light(XMLReadState &state, const xml_node node)
|
||||
{
|
||||
Scene *scene = state.scene;
|
||||
|
||||
/* Create light. */
|
||||
string light_type;
|
||||
if (!xml_read_string(&light_type, node, "light_type")) {
|
||||
return;
|
||||
}
|
||||
|
||||
Light *light;
|
||||
if (light_type == "point") {
|
||||
light = scene->create_node<PointLight>();
|
||||
}
|
||||
else if (light_type == "sun") {
|
||||
light = scene->create_node<SunLight>();
|
||||
}
|
||||
else if (light_type == "background") {
|
||||
light = scene->create_node<BackgroundLight>();
|
||||
}
|
||||
else if (light_type == "area") {
|
||||
light = scene->create_node<AreaLight>();
|
||||
}
|
||||
else {
|
||||
assert(light_type == "spot");
|
||||
light = scene->create_node<SpotLight>();
|
||||
}
|
||||
|
||||
array<Node *> used_shaders;
|
||||
used_shaders.push_back_slow(state.shader);
|
||||
light->set_used_shaders(used_shaders);
|
||||
|
||||
/* Create object. */
|
||||
Object *object = scene->create_node<Object>();
|
||||
object->set_tfm(state.tfm);
|
||||
object->set_visibility(PATH_RAY_VISIBILITY_ALL & ~PATH_RAY_VISIBILITY_CAMERA);
|
||||
object->set_geometry(light);
|
||||
|
||||
xml_read_node(state, light, node);
|
||||
}
|
||||
|
||||
/* Transform */
|
||||
|
||||
static void xml_read_transform(const xml_node node, Transform &tfm)
|
||||
{
|
||||
if (node.attribute("matrix")) {
|
||||
vector<float> matrix;
|
||||
if (xml_read_float_array(matrix, node, "matrix") && matrix.size() == 16) {
|
||||
const ProjectionTransform projection = *(ProjectionTransform *)matrix.data();
|
||||
tfm = tfm * projection_to_transform(projection_transpose(projection));
|
||||
}
|
||||
}
|
||||
|
||||
if (node.attribute("translate")) {
|
||||
float3 translate = zero_float3();
|
||||
xml_read_float3(&translate, node, "translate");
|
||||
tfm = tfm * transform_translate(translate);
|
||||
}
|
||||
|
||||
if (node.attribute("rotate")) {
|
||||
float4 rotate = zero_float4();
|
||||
xml_read_float4(&rotate, node, "rotate");
|
||||
tfm = tfm * transform_rotate(DEG2RADF(rotate.x), make_float3(rotate.y, rotate.z, rotate.w));
|
||||
}
|
||||
|
||||
if (node.attribute("scale")) {
|
||||
float3 scale = zero_float3();
|
||||
xml_read_float3(&scale, node, "scale");
|
||||
tfm = tfm * transform_scale(scale);
|
||||
}
|
||||
}
|
||||
|
||||
/* State */
|
||||
|
||||
static void xml_read_state(XMLReadState &state, const xml_node node)
|
||||
{
|
||||
/* Read shader */
|
||||
string shadername;
|
||||
|
||||
if (xml_read_string(&shadername, node, "shader")) {
|
||||
bool found = false;
|
||||
|
||||
for (Shader *shader : state.scene->shaders) {
|
||||
if (shader->name == shadername) {
|
||||
state.shader = shader;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
LOG_ERROR << "Unknown shader \"" << shadername << "\"";
|
||||
}
|
||||
}
|
||||
|
||||
/* Read object */
|
||||
string objectname;
|
||||
|
||||
if (xml_read_string(&objectname, node, "object")) {
|
||||
bool found = false;
|
||||
|
||||
for (Object *object : state.scene->objects) {
|
||||
if (object->name == objectname) {
|
||||
state.object = object;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
LOG_ERROR << "Unknown object \"" << objectname << "\"";
|
||||
}
|
||||
}
|
||||
|
||||
xml_read_float(&state.dicing_rate, node, "dicing_rate");
|
||||
|
||||
/* read smooth/flat */
|
||||
if (xml_equal_string(node, "interpolation", "smooth")) {
|
||||
state.smooth = true;
|
||||
}
|
||||
else if (xml_equal_string(node, "interpolation", "flat")) {
|
||||
state.smooth = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Object */
|
||||
|
||||
static void xml_read_object(XMLReadState &state, const xml_node node)
|
||||
{
|
||||
Scene *scene = state.scene;
|
||||
|
||||
/* create mesh */
|
||||
Mesh *mesh = scene->create_node<Mesh>();
|
||||
|
||||
/* create object */
|
||||
Object *object = scene->create_node<Object>();
|
||||
object->set_geometry(mesh);
|
||||
object->set_tfm(state.tfm);
|
||||
|
||||
xml_read_node(state, object, node);
|
||||
}
|
||||
|
||||
/* Scene */
|
||||
|
||||
static void xml_read_include(XMLReadState &state, const string &src);
|
||||
|
||||
static void xml_read_scene(XMLReadState &state, const xml_node scene_node)
|
||||
{
|
||||
for (xml_node node = scene_node.first_child(); node; node = node.next_sibling()) {
|
||||
if (string_iequals(node.name(), "film")) {
|
||||
xml_read_node(state, state.scene->film, node);
|
||||
}
|
||||
else if (string_iequals(node.name(), "integrator")) {
|
||||
xml_read_node(state, state.scene->integrator, node);
|
||||
}
|
||||
else if (string_iequals(node.name(), "camera")) {
|
||||
xml_read_camera(state, node);
|
||||
}
|
||||
else if (string_iequals(node.name(), "shader")) {
|
||||
xml_read_shader(state, node);
|
||||
}
|
||||
else if (string_iequals(node.name(), "background")) {
|
||||
xml_read_background(state, node);
|
||||
}
|
||||
else if (string_iequals(node.name(), "mesh")) {
|
||||
xml_read_mesh(state, node);
|
||||
}
|
||||
else if (string_iequals(node.name(), "light")) {
|
||||
xml_read_light(state, node);
|
||||
}
|
||||
else if (string_iequals(node.name(), "transform")) {
|
||||
XMLReadState substate = state;
|
||||
|
||||
xml_read_transform(node, substate.tfm);
|
||||
xml_read_scene(substate, node);
|
||||
}
|
||||
else if (string_iequals(node.name(), "state")) {
|
||||
XMLReadState substate = state;
|
||||
|
||||
xml_read_state(substate, node);
|
||||
xml_read_scene(substate, node);
|
||||
}
|
||||
else if (string_iequals(node.name(), "include")) {
|
||||
string src;
|
||||
|
||||
if (xml_read_string(&src, node, "src")) {
|
||||
xml_read_include(state, src);
|
||||
}
|
||||
}
|
||||
else if (string_iequals(node.name(), "object")) {
|
||||
XMLReadState substate = state;
|
||||
|
||||
xml_read_object(substate, node);
|
||||
xml_read_scene(substate, node);
|
||||
}
|
||||
else {
|
||||
LOG_ERROR << "Unknown node \"" << node.name() << "\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Include */
|
||||
|
||||
static void xml_read_include(XMLReadState &state, const string &src)
|
||||
{
|
||||
/* open XML document */
|
||||
xml_document doc;
|
||||
xml_parse_result parse_result;
|
||||
|
||||
const string path = path_join(state.base, src);
|
||||
parse_result = doc.load_file(path.c_str());
|
||||
|
||||
if (parse_result) {
|
||||
XMLReadState substate = state;
|
||||
substate.base = path_dirname(path);
|
||||
|
||||
const xml_node cycles = doc.child("cycles");
|
||||
xml_read_scene(substate, cycles);
|
||||
}
|
||||
else {
|
||||
LOG_ERROR << "\"" << src << "\" read error: " << parse_result.description();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/* File */
|
||||
|
||||
void xml_read_file(Scene *scene, const char *filepath)
|
||||
{
|
||||
XMLReadState state;
|
||||
|
||||
state.scene = scene;
|
||||
state.tfm = transform_identity();
|
||||
state.shader = scene->default_surface;
|
||||
state.smooth = false;
|
||||
state.dicing_rate = 1.0f;
|
||||
state.base = path_dirname(filepath);
|
||||
|
||||
xml_read_include(state, path_filename(filepath));
|
||||
|
||||
scene->params.bvh_type = BVH_TYPE_STATIC;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
19
blender-5.2.0/intern/cycles/app/cycles_xml.h
Normal file
19
blender-5.2.0/intern/cycles/app/cycles_xml.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/math_base.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class Scene;
|
||||
|
||||
void xml_read_file(Scene *scene, const char *filepath);
|
||||
|
||||
/* macros for importing */
|
||||
#define RAD2DEGF(_rad) ((_rad) * (float)(180.0f / M_PI_F))
|
||||
#define DEG2RADF(_deg) ((_deg) * (float)(M_PI_F / 180.0f))
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
153
blender-5.2.0/intern/cycles/app/io_export_cycles_xml.py
Normal file
153
blender-5.2.0/intern/cycles/app/io_export_cycles_xml.py
Normal file
@@ -0,0 +1,153 @@
|
||||
# SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# XML exporter for generating test files, not intended for end users
|
||||
|
||||
import xml.etree.ElementTree as etree
|
||||
import xml.dom.minidom as dom
|
||||
|
||||
import bpy
|
||||
from bpy_extras.io_utils import ExportHelper
|
||||
from bpy.props import PointerProperty, StringProperty
|
||||
|
||||
|
||||
def strip(root):
|
||||
root.text = None
|
||||
root.tail = None
|
||||
|
||||
for elem in root:
|
||||
strip(elem)
|
||||
|
||||
|
||||
def write(node, fname):
|
||||
strip(node)
|
||||
|
||||
s = etree.tostring(node)
|
||||
s = dom.parseString(s).toprettyxml()
|
||||
|
||||
f = open(fname, "w")
|
||||
f.write(s)
|
||||
|
||||
|
||||
class CyclesXMLSettings(bpy.types.PropertyGroup):
|
||||
@classmethod
|
||||
def register(cls):
|
||||
bpy.types.Scene.cycles_xml = PointerProperty(
|
||||
type=cls,
|
||||
name="Cycles XML export Settings",
|
||||
description="Cycles XML export settings",
|
||||
)
|
||||
cls.filepath = StringProperty(
|
||||
name='Filepath',
|
||||
description='Filepath for the .xml file',
|
||||
maxlen=256,
|
||||
default='',
|
||||
subtype='FILE_PATH',
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def unregister(cls):
|
||||
del bpy.types.Scene.cycles_xml
|
||||
|
||||
|
||||
# User Interface Drawing Code.
|
||||
|
||||
|
||||
class RenderButtonsPanel:
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "render"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.engine == 'CYCLES'
|
||||
|
||||
|
||||
class PHYSICS_PT_fluid_export(RenderButtonsPanel, bpy.types.Panel):
|
||||
bl_label = "Cycles XML Exporter"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
cycles = context.scene.cycles_xml
|
||||
|
||||
# layout.prop(cycles, "filepath")
|
||||
layout.operator("export_mesh.cycles_xml")
|
||||
|
||||
|
||||
# Export Operator
|
||||
class ExportCyclesXML(bpy.types.Operator, ExportHelper):
|
||||
bl_idname = "export_mesh.cycles_xml"
|
||||
bl_label = "Export Cycles XML"
|
||||
|
||||
filename_ext = ".xml"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (context.active_object is not None)
|
||||
|
||||
def execute(self, context):
|
||||
filepath = bpy.path.ensure_ext(self.filepath, ".xml")
|
||||
|
||||
# get mesh
|
||||
scene = context.scene
|
||||
object = context.active_object
|
||||
|
||||
if not object:
|
||||
raise Exception("No active object")
|
||||
|
||||
mesh = object.to_mesh(scene, True, 'PREVIEW')
|
||||
|
||||
if not mesh:
|
||||
raise Exception("No mesh data in active object")
|
||||
|
||||
# generate mesh node
|
||||
nverts = ""
|
||||
verts = ""
|
||||
uvs = ""
|
||||
P = ""
|
||||
|
||||
for v in mesh.vertices:
|
||||
P += "%f %f %f " % (v.co[0], v.co[1], v.co[2])
|
||||
|
||||
verts_and_uvs = zip(mesh.tessfaces, mesh.tessface_uv_textures.active.data)
|
||||
|
||||
for f, uvf in verts_and_uvs:
|
||||
vcount = len(f.vertices)
|
||||
nverts += str(vcount) + " "
|
||||
|
||||
for v in f.vertices:
|
||||
verts += str(v) + " "
|
||||
|
||||
uvs += str(uvf.uv1[0]) + " " + str(uvf.uv1[1]) + " "
|
||||
uvs += str(uvf.uv2[0]) + " " + str(uvf.uv2[1]) + " "
|
||||
uvs += str(uvf.uv3[0]) + " " + str(uvf.uv3[1]) + " "
|
||||
if vcount == 4:
|
||||
uvs += " " + str(uvf.uv4[0]) + " " + str(uvf.uv4[1]) + " "
|
||||
|
||||
node = etree.Element(
|
||||
'mesh',
|
||||
attrib={
|
||||
'nverts': nverts.strip(),
|
||||
'verts': verts.strip(),
|
||||
'P': P,
|
||||
'UV': uvs.strip(),
|
||||
})
|
||||
|
||||
# write to file
|
||||
write(node, filepath)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_module(__name__)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_module(__name__)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
||||
77
blender-5.2.0/intern/cycles/app/oiio_output_driver.cpp
Normal file
77
blender-5.2.0/intern/cycles/app/oiio_output_driver.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "app/oiio_output_driver.h"
|
||||
|
||||
#include "util/colorspace.h"
|
||||
#include "util/image.h"
|
||||
#include "util/unique_ptr.h"
|
||||
|
||||
#include <OpenImageIO/imagebuf.h>
|
||||
#include <OpenImageIO/imagebufalgo.h>
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
OIIOOutputDriver::OIIOOutputDriver(const string_view filepath,
|
||||
const string_view pass,
|
||||
LogFunction log)
|
||||
: filepath_(filepath), pass_(pass), log_(log)
|
||||
{
|
||||
}
|
||||
|
||||
OIIOOutputDriver::~OIIOOutputDriver() = default;
|
||||
|
||||
void OIIOOutputDriver::write_render_tile(const Tile &tile)
|
||||
{
|
||||
/* Only write the full buffer, no intermediate tiles. */
|
||||
if (!(tile.size == tile.full_size)) {
|
||||
return;
|
||||
}
|
||||
|
||||
log_(string_printf("Writing image %s", filepath_.c_str()));
|
||||
|
||||
unique_ptr<ImageOutput> image_output(ImageOutput::create(filepath_));
|
||||
if (image_output == nullptr) {
|
||||
log_("Failed to create image file");
|
||||
return;
|
||||
}
|
||||
|
||||
const int width = tile.size.x;
|
||||
const int height = tile.size.y;
|
||||
|
||||
const ImageSpec spec(width, height, 4, TypeDesc::FLOAT);
|
||||
if (!image_output->open(filepath_, spec)) {
|
||||
log_("Failed to create image file");
|
||||
return;
|
||||
}
|
||||
|
||||
vector<float> pixels(width * height * 4);
|
||||
if (!tile.get_pass_pixels(pass_, 4, pixels.data())) {
|
||||
log_("Failed to read render pass pixels");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Manipulate offset and stride to convert from bottom-up to top-down convention. */
|
||||
OIIO::ImageBuf image_buffer(spec,
|
||||
pixels.data() + (height - 1) * width * 4,
|
||||
AutoStride,
|
||||
-width * 4 * sizeof(float),
|
||||
AutoStride);
|
||||
|
||||
/* Apply gamma correction for (some) non-linear file formats.
|
||||
* TODO: use OpenColorIO view transform if available. */
|
||||
if (ColorSpaceManager::detect_known_colorspace(
|
||||
u_colorspace_auto, "", image_output->format_name(), true) == u_colorspace_srgb)
|
||||
{
|
||||
const float g = 1.0f / 2.2f;
|
||||
OIIO::ImageBufAlgo::pow(image_buffer, image_buffer, {g, g, g, 1.0f});
|
||||
}
|
||||
|
||||
/* Write to disk and close */
|
||||
image_buffer.set_write_format(TypeDesc::FLOAT);
|
||||
image_buffer.write(image_output.get());
|
||||
image_output->close();
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
28
blender-5.2.0/intern/cycles/app/oiio_output_driver.h
Normal file
28
blender-5.2.0/intern/cycles/app/oiio_output_driver.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "session/output_driver.h"
|
||||
|
||||
#include "util/string.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class OIIOOutputDriver : public OutputDriver {
|
||||
public:
|
||||
using LogFunction = std::function<void(const string &)>;
|
||||
|
||||
OIIOOutputDriver(const string_view filepath, const string_view pass, LogFunction log);
|
||||
~OIIOOutputDriver() override;
|
||||
|
||||
void write_render_tile(const Tile &tile) override;
|
||||
|
||||
protected:
|
||||
string filepath_;
|
||||
string pass_;
|
||||
LogFunction log_;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
401
blender-5.2.0/intern/cycles/app/opengl/display_driver.cpp
Normal file
401
blender-5.2.0/intern/cycles/app/opengl/display_driver.cpp
Normal file
@@ -0,0 +1,401 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "app/opengl/display_driver.h"
|
||||
#include "app/opengl/shader.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* OpenGLDisplayDriver.
|
||||
*/
|
||||
|
||||
OpenGLDisplayDriver::OpenGLDisplayDriver(const std::function<bool()> &gl_context_enable,
|
||||
const std::function<void()> &gl_context_disable)
|
||||
: gl_context_enable_(gl_context_enable), gl_context_disable_(gl_context_disable)
|
||||
{
|
||||
}
|
||||
|
||||
OpenGLDisplayDriver::~OpenGLDisplayDriver() = default;
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Update procedure.
|
||||
*/
|
||||
|
||||
void OpenGLDisplayDriver::next_tile_begin()
|
||||
{
|
||||
/* Assuming no tiles used in interactive display. */
|
||||
}
|
||||
|
||||
bool OpenGLDisplayDriver::update_begin(const Params ¶ms,
|
||||
const int texture_width,
|
||||
const int texture_height)
|
||||
{
|
||||
/* Note that it's the responsibility of OpenGLDisplayDriver to ensure updating and drawing
|
||||
* the texture does not happen at the same time. This is achieved indirectly.
|
||||
*
|
||||
* When enabling the OpenGL context, it uses an internal mutex lock DST.gl_context_lock.
|
||||
* This same lock is also held when do_draw() is called, which together ensure mutual
|
||||
* exclusion.
|
||||
*
|
||||
* This locking is not performed on the Cycles side, because that would cause lock inversion. */
|
||||
if (!gl_context_enable_()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gl_render_sync_) {
|
||||
glWaitSync((GLsync)gl_render_sync_, 0, GL_TIMEOUT_IGNORED);
|
||||
}
|
||||
|
||||
if (!gl_texture_resources_ensure()) {
|
||||
gl_context_disable_();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Update texture dimensions if needed. */
|
||||
if (texture_.width != texture_width || texture_.height != texture_height) {
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, texture_.gl_id);
|
||||
glTexImage2D(GL_TEXTURE_2D,
|
||||
0,
|
||||
GL_RGBA16F,
|
||||
texture_width,
|
||||
texture_height,
|
||||
0,
|
||||
GL_RGBA,
|
||||
GL_HALF_FLOAT,
|
||||
nullptr);
|
||||
texture_.width = texture_width;
|
||||
texture_.height = texture_height;
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
/* Texture did change, and no pixel storage was provided. Tag for an explicit zeroing out to
|
||||
* avoid undefined content. */
|
||||
texture_.need_zero = true;
|
||||
graphics_interop_buffer_.clear();
|
||||
}
|
||||
|
||||
/* Update PBO dimensions if needed.
|
||||
*
|
||||
* NOTE: Allocate the PBO for the size which will fit the final render resolution (as in,
|
||||
* at a resolution divider 1. This was we don't need to recreate graphics interoperability
|
||||
* objects which are costly and which are tied to the specific underlying buffer size.
|
||||
* The downside of this approach is that when graphics interoperability is not used we are
|
||||
* sending too much data to GPU when resolution divider is not 1. */
|
||||
const int buffer_width = params.full_size.x;
|
||||
const int buffer_height = params.full_size.y;
|
||||
if (texture_.buffer_width != buffer_width || texture_.buffer_height != buffer_height) {
|
||||
const size_t size_in_bytes = sizeof(half4) * buffer_width * buffer_height;
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id);
|
||||
glBufferData(GL_PIXEL_UNPACK_BUFFER, size_in_bytes, nullptr, GL_DYNAMIC_DRAW);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
|
||||
texture_.buffer_width = buffer_width;
|
||||
texture_.buffer_height = buffer_height;
|
||||
}
|
||||
|
||||
/* New content will be provided to the texture in one way or another, so mark this in a
|
||||
* centralized place. */
|
||||
texture_.need_update = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenGLDisplayDriver::update_end()
|
||||
{
|
||||
gl_upload_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
glFlush();
|
||||
|
||||
gl_context_disable_();
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Texture buffer mapping.
|
||||
*/
|
||||
|
||||
half4 *OpenGLDisplayDriver::map_texture_buffer()
|
||||
{
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id);
|
||||
|
||||
half4 *mapped_rgba_pixels = reinterpret_cast<half4 *>(
|
||||
glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY));
|
||||
if (!mapped_rgba_pixels) {
|
||||
LOG_ERROR << "Error mapping OpenGLDisplayDriver pixel buffer object.";
|
||||
}
|
||||
|
||||
if (texture_.need_zero) {
|
||||
const int64_t texture_width = texture_.width;
|
||||
const int64_t texture_height = texture_.height;
|
||||
memset(reinterpret_cast<void *>(mapped_rgba_pixels),
|
||||
0,
|
||||
texture_width * texture_height * sizeof(half4));
|
||||
texture_.need_zero = false;
|
||||
}
|
||||
|
||||
return mapped_rgba_pixels;
|
||||
}
|
||||
|
||||
void OpenGLDisplayDriver::unmap_texture_buffer()
|
||||
{
|
||||
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Graphics interoperability.
|
||||
*/
|
||||
|
||||
GraphicsInteropDevice OpenGLDisplayDriver::graphics_interop_get_device()
|
||||
{
|
||||
GraphicsInteropDevice interop_device;
|
||||
interop_device.type = GraphicsInteropDevice::OPENGL;
|
||||
return interop_device;
|
||||
}
|
||||
|
||||
void OpenGLDisplayDriver::graphics_interop_update_buffer()
|
||||
{
|
||||
if (graphics_interop_buffer_.is_empty()) {
|
||||
graphics_interop_buffer_.assign(GraphicsInteropDevice::OPENGL,
|
||||
texture_.gl_pbo_id,
|
||||
texture_.buffer_width * texture_.buffer_height *
|
||||
sizeof(half4));
|
||||
}
|
||||
|
||||
if (texture_.need_zero) {
|
||||
graphics_interop_buffer_.zero();
|
||||
texture_.need_zero = false;
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLDisplayDriver::graphics_interop_activate()
|
||||
{
|
||||
gl_context_enable_();
|
||||
}
|
||||
|
||||
void OpenGLDisplayDriver::graphics_interop_deactivate()
|
||||
{
|
||||
gl_context_disable_();
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Drawing.
|
||||
*/
|
||||
|
||||
void OpenGLDisplayDriver::zero()
|
||||
{
|
||||
texture_.need_zero = true;
|
||||
}
|
||||
|
||||
void OpenGLDisplayDriver::draw(const Params ¶ms)
|
||||
{
|
||||
/* See do_update_begin() for why no locking is required here. */
|
||||
if (texture_.need_zero) {
|
||||
/* Texture is requested to be cleared and was not yet cleared.
|
||||
* Do early return which should be equivalent of drawing all-zero texture. */
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gl_draw_resources_ensure()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (gl_upload_sync_) {
|
||||
glWaitSync((GLsync)gl_upload_sync_, 0, GL_TIMEOUT_IGNORED);
|
||||
}
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
display_shader_.bind(params.full_size.x, params.full_size.y);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, texture_.gl_id);
|
||||
|
||||
if (texture_.width != params.size.x || texture_.height != params.size.y) {
|
||||
/* Resolution divider is different from 1, force nearest interpolation. */
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
}
|
||||
else {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_);
|
||||
|
||||
texture_update_if_needed();
|
||||
vertex_buffer_update(params);
|
||||
|
||||
GLuint vertex_array_object;
|
||||
glGenVertexArrays(1, &vertex_array_object);
|
||||
glBindVertexArray(vertex_array_object);
|
||||
|
||||
const int texcoord_attribute = display_shader_.get_tex_coord_attrib_location();
|
||||
const int position_attribute = display_shader_.get_position_attrib_location();
|
||||
|
||||
glEnableVertexAttribArray(texcoord_attribute);
|
||||
glEnableVertexAttribArray(position_attribute);
|
||||
|
||||
glVertexAttribPointer(
|
||||
texcoord_attribute, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (const GLvoid *)nullptr);
|
||||
glVertexAttribPointer(position_attribute,
|
||||
2,
|
||||
GL_FLOAT,
|
||||
GL_FALSE,
|
||||
4 * sizeof(float),
|
||||
(const GLvoid *)(sizeof(float) * 2));
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
glDeleteVertexArrays(1, &vertex_array_object);
|
||||
|
||||
display_shader_.unbind();
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
gl_render_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
glFlush();
|
||||
}
|
||||
|
||||
bool OpenGLDisplayDriver::gl_draw_resources_ensure()
|
||||
{
|
||||
if (!texture_.gl_id) {
|
||||
/* If there is no texture allocated, there is nothing to draw. Inform the draw call that it can
|
||||
* can not continue. Note that this is not an unrecoverable error, so once the texture is known
|
||||
* we will come back here and create all the GPU resources needed for draw. */
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gl_draw_resource_creation_attempted_) {
|
||||
return gl_draw_resources_created_;
|
||||
}
|
||||
gl_draw_resource_creation_attempted_ = true;
|
||||
|
||||
if (!vertex_buffer_) {
|
||||
glGenBuffers(1, &vertex_buffer_);
|
||||
if (!vertex_buffer_) {
|
||||
LOG_ERROR << "Error creating vertex buffer.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
gl_draw_resources_created_ = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenGLDisplayDriver::gl_resources_destroy()
|
||||
{
|
||||
gl_context_enable_();
|
||||
|
||||
if (vertex_buffer_ != 0) {
|
||||
glDeleteBuffers(1, &vertex_buffer_);
|
||||
}
|
||||
|
||||
if (texture_.gl_pbo_id) {
|
||||
glDeleteBuffers(1, &texture_.gl_pbo_id);
|
||||
texture_.gl_pbo_id = 0;
|
||||
}
|
||||
|
||||
if (texture_.gl_id) {
|
||||
glDeleteTextures(1, &texture_.gl_id);
|
||||
texture_.gl_id = 0;
|
||||
}
|
||||
|
||||
gl_context_disable_();
|
||||
}
|
||||
|
||||
bool OpenGLDisplayDriver::gl_texture_resources_ensure()
|
||||
{
|
||||
if (texture_.creation_attempted) {
|
||||
return texture_.is_created;
|
||||
}
|
||||
texture_.creation_attempted = true;
|
||||
|
||||
DCHECK(!texture_.gl_id);
|
||||
DCHECK(!texture_.gl_pbo_id);
|
||||
|
||||
/* Create texture. */
|
||||
glGenTextures(1, &texture_.gl_id);
|
||||
if (!texture_.gl_id) {
|
||||
LOG_ERROR << "Error creating texture.";
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Configure the texture. */
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, texture_.gl_id);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
/* Create PBO for the texture. */
|
||||
glGenBuffers(1, &texture_.gl_pbo_id);
|
||||
if (!texture_.gl_pbo_id) {
|
||||
LOG_ERROR << "Error creating texture pixel buffer object.";
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Creation finished with a success. */
|
||||
texture_.is_created = true;
|
||||
graphics_interop_buffer_.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenGLDisplayDriver::texture_update_if_needed()
|
||||
{
|
||||
if (!texture_.need_update) {
|
||||
return;
|
||||
}
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id);
|
||||
glTexSubImage2D(
|
||||
GL_TEXTURE_2D, 0, 0, 0, texture_.width, texture_.height, GL_RGBA, GL_HALF_FLOAT, nullptr);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
|
||||
texture_.need_update = false;
|
||||
}
|
||||
|
||||
void OpenGLDisplayDriver::vertex_buffer_update(const Params ¶ms)
|
||||
{
|
||||
/* Invalidate old contents - avoids stalling if the buffer is still waiting in queue to be
|
||||
* rendered. */
|
||||
glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(float), nullptr, GL_STREAM_DRAW);
|
||||
|
||||
float *vpointer = reinterpret_cast<float *>(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY));
|
||||
if (!vpointer) {
|
||||
return;
|
||||
}
|
||||
|
||||
vpointer[0] = 0.0f;
|
||||
vpointer[1] = 0.0f;
|
||||
vpointer[2] = params.full_offset.x;
|
||||
vpointer[3] = params.full_offset.y;
|
||||
|
||||
vpointer[4] = 1.0f;
|
||||
vpointer[5] = 0.0f;
|
||||
vpointer[6] = (float)params.size.x + params.full_offset.x;
|
||||
vpointer[7] = params.full_offset.y;
|
||||
|
||||
vpointer[8] = 1.0f;
|
||||
vpointer[9] = 1.0f;
|
||||
vpointer[10] = (float)params.size.x + params.full_offset.x;
|
||||
vpointer[11] = (float)params.size.y + params.full_offset.y;
|
||||
|
||||
vpointer[12] = 0.0f;
|
||||
vpointer[13] = 1.0f;
|
||||
vpointer[14] = params.full_offset.x;
|
||||
vpointer[15] = (float)params.size.y + params.full_offset.y;
|
||||
|
||||
glUnmapBuffer(GL_ARRAY_BUFFER);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
119
blender-5.2.0/intern/cycles/app/opengl/display_driver.h
Normal file
119
blender-5.2.0/intern/cycles/app/opengl/display_driver.h
Normal file
@@ -0,0 +1,119 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
|
||||
#include "app/opengl/shader.h"
|
||||
|
||||
#include "session/display_driver.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class OpenGLDisplayDriver : public DisplayDriver {
|
||||
public:
|
||||
/* Callbacks for enabling and disabling the OpenGL context. Must be provided to support enabling
|
||||
* the context on the Cycles render thread independent of the main thread. */
|
||||
OpenGLDisplayDriver(const std::function<bool()> &gl_context_enable,
|
||||
const std::function<void()> &gl_context_disable);
|
||||
~OpenGLDisplayDriver() override;
|
||||
|
||||
void graphics_interop_activate() override;
|
||||
void graphics_interop_deactivate() override;
|
||||
|
||||
void zero() override;
|
||||
|
||||
void set_zoom(const float zoom_x, const float zoom_y);
|
||||
|
||||
protected:
|
||||
void next_tile_begin() override;
|
||||
|
||||
bool update_begin(const Params ¶ms,
|
||||
const int texture_width,
|
||||
const int texture_height) override;
|
||||
void update_end() override;
|
||||
|
||||
half4 *map_texture_buffer() override;
|
||||
void unmap_texture_buffer() override;
|
||||
|
||||
GraphicsInteropDevice graphics_interop_get_device() override;
|
||||
void graphics_interop_update_buffer() override;
|
||||
|
||||
void draw(const Params ¶ms) override;
|
||||
|
||||
/* Make sure texture is allocated and its initial configuration is performed. */
|
||||
bool gl_texture_resources_ensure();
|
||||
|
||||
/* Ensure all runtime GPU resources needed for drawing are allocated.
|
||||
* Returns true if all resources needed for drawing are available. */
|
||||
bool gl_draw_resources_ensure();
|
||||
|
||||
/* Destroy all GPU resources which are being used by this object. */
|
||||
void gl_resources_destroy();
|
||||
|
||||
/* Update GPU texture dimensions and content if needed (new pixel data was provided).
|
||||
*
|
||||
* NOTE: The texture needs to be bound. */
|
||||
void texture_update_if_needed();
|
||||
|
||||
/* Update vertex buffer with new coordinates of vertex positions and texture coordinates.
|
||||
* This buffer is used to render texture in the viewport.
|
||||
*
|
||||
* NOTE: The buffer needs to be bound. */
|
||||
void vertex_buffer_update(const Params ¶ms);
|
||||
|
||||
/* Texture which contains pixels of the render result. */
|
||||
struct {
|
||||
/* Indicates whether texture creation was attempted and succeeded.
|
||||
* Used to avoid multiple attempts of texture creation on GPU issues or GPU context
|
||||
* misconfiguration. */
|
||||
bool creation_attempted = false;
|
||||
bool is_created = false;
|
||||
|
||||
/* OpenGL resource IDs of the texture itself and Pixel Buffer Object (PBO) used to write
|
||||
* pixels to it.
|
||||
*
|
||||
* NOTE: Allocated on the engine's context. */
|
||||
uint gl_id = 0;
|
||||
uint gl_pbo_id = 0;
|
||||
|
||||
/* Is true when new data was written to the PBO, meaning, the texture might need to be resized
|
||||
* and new data is to be uploaded to the GPU. */
|
||||
bool need_update = false;
|
||||
|
||||
/* Content of the texture is to be filled with zeroes. */
|
||||
std::atomic<bool> need_zero = true;
|
||||
|
||||
/* Dimensions of the texture in pixels. */
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
|
||||
/* Dimensions of the underlying PBO. */
|
||||
int buffer_width = 0;
|
||||
int buffer_height = 0;
|
||||
} texture_;
|
||||
|
||||
OpenGLShader display_shader_;
|
||||
|
||||
/* Special track of whether GPU resources were attempted to be created, to avoid attempts of
|
||||
* their re-creation on failure on every redraw. */
|
||||
bool gl_draw_resource_creation_attempted_ = false;
|
||||
bool gl_draw_resources_created_ = false;
|
||||
|
||||
/* Vertex buffer which hold vertices of a triangle fan which is textures with the texture
|
||||
* holding the render result. */
|
||||
uint vertex_buffer_ = 0;
|
||||
|
||||
void *gl_render_sync_ = nullptr;
|
||||
void *gl_upload_sync_ = nullptr;
|
||||
|
||||
float2 zoom_ = make_float2(1.0f, 1.0f);
|
||||
|
||||
std::function<bool()> gl_context_enable_ = nullptr;
|
||||
std::function<void()> gl_context_disable_ = nullptr;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
198
blender-5.2.0/intern/cycles/app/opengl/shader.cpp
Normal file
198
blender-5.2.0/intern/cycles/app/opengl/shader.cpp
Normal file
@@ -0,0 +1,198 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "app/opengl/shader.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/string.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* OpenGLShader.
|
||||
*/
|
||||
|
||||
static const char *VERTEX_SHADER =
|
||||
"#version 330\n"
|
||||
"uniform vec2 fullscreen;\n"
|
||||
"in vec2 texCoord;\n"
|
||||
"in vec2 pos;\n"
|
||||
"out vec2 texCoord_interp;\n"
|
||||
"\n"
|
||||
"vec2 normalize_coordinates()\n"
|
||||
"{\n"
|
||||
" return (vec2(2.0) * (pos / fullscreen)) - vec2(1.0);\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" gl_Position = vec4(normalize_coordinates(), 0.0, 1.0);\n"
|
||||
" texCoord_interp = texCoord;\n"
|
||||
"}\n\0";
|
||||
|
||||
static const char *FRAGMENT_SHADER =
|
||||
"#version 330\n"
|
||||
"uniform sampler2D image_texture;\n"
|
||||
"in vec2 texCoord_interp;\n"
|
||||
"out vec4 fragColor;\n"
|
||||
"\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" vec4 rgba = texture(image_texture, texCoord_interp);\n"
|
||||
/* Hard-coded Rec.709 gamma, should use OpenColorIO eventually. */
|
||||
" fragColor = pow(rgba, vec4(0.45, 0.45, 0.45, 1.0));\n"
|
||||
"}\n\0";
|
||||
|
||||
static void shader_print_errors(const char *task, const char *log, const char *code)
|
||||
{
|
||||
LOG_ERROR << "Shader: " << task << " error:";
|
||||
LOG_ERROR << "===== shader string ====";
|
||||
|
||||
std::stringstream stream(code);
|
||||
string partial;
|
||||
|
||||
int line = 1;
|
||||
while (getline(stream, partial, '\n')) {
|
||||
if (line < 10) {
|
||||
LOG_ERROR << " " << line << " " << partial;
|
||||
}
|
||||
else {
|
||||
LOG_ERROR << line << " " << partial;
|
||||
}
|
||||
line++;
|
||||
}
|
||||
LOG_ERROR << log;
|
||||
}
|
||||
|
||||
static int compile_shader_program()
|
||||
{
|
||||
const struct Shader {
|
||||
const char *source;
|
||||
const GLenum type;
|
||||
} shaders[2] = {{VERTEX_SHADER, GL_VERTEX_SHADER}, {FRAGMENT_SHADER, GL_FRAGMENT_SHADER}};
|
||||
|
||||
const GLuint program = glCreateProgram();
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
const GLuint shader = glCreateShader(shaders[i].type);
|
||||
|
||||
const string source_str = shaders[i].source;
|
||||
const char *c_str = source_str.c_str();
|
||||
|
||||
glShaderSource(shader, 1, &c_str, nullptr);
|
||||
glCompileShader(shader);
|
||||
|
||||
GLint compile_status;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
|
||||
|
||||
if (!compile_status) {
|
||||
GLchar log[5000];
|
||||
GLsizei length = 0;
|
||||
glGetShaderInfoLog(shader, sizeof(log), &length, log);
|
||||
shader_print_errors("compile", log, c_str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
glAttachShader(program, shader);
|
||||
}
|
||||
|
||||
/* Link output. */
|
||||
glBindFragDataLocation(program, 0, "fragColor");
|
||||
|
||||
/* Link and error check. */
|
||||
glLinkProgram(program);
|
||||
|
||||
GLint link_status;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &link_status);
|
||||
if (!link_status) {
|
||||
GLchar log[5000];
|
||||
GLsizei length = 0;
|
||||
glGetShaderInfoLog(program, sizeof(log), &length, log);
|
||||
shader_print_errors("linking", log, VERTEX_SHADER);
|
||||
shader_print_errors("linking", log, FRAGMENT_SHADER);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
int OpenGLShader::get_position_attrib_location()
|
||||
{
|
||||
if (position_attribute_location_ == -1) {
|
||||
const uint shader_program = get_shader_program();
|
||||
position_attribute_location_ = glGetAttribLocation(shader_program, position_attribute_name);
|
||||
}
|
||||
return position_attribute_location_;
|
||||
}
|
||||
|
||||
int OpenGLShader::get_tex_coord_attrib_location()
|
||||
{
|
||||
if (tex_coord_attribute_location_ == -1) {
|
||||
const uint shader_program = get_shader_program();
|
||||
tex_coord_attribute_location_ = glGetAttribLocation(shader_program, tex_coord_attribute_name);
|
||||
}
|
||||
return tex_coord_attribute_location_;
|
||||
}
|
||||
|
||||
void OpenGLShader::bind(const int width, const int height)
|
||||
{
|
||||
create_shader_if_needed();
|
||||
|
||||
if (!shader_program_) {
|
||||
return;
|
||||
}
|
||||
|
||||
glUseProgram(shader_program_);
|
||||
glUniform1i(image_texture_location_, 0);
|
||||
glUniform2f(fullscreen_location_, width, height);
|
||||
}
|
||||
|
||||
void OpenGLShader::unbind() {}
|
||||
|
||||
uint OpenGLShader::get_shader_program()
|
||||
{
|
||||
return shader_program_;
|
||||
}
|
||||
|
||||
void OpenGLShader::create_shader_if_needed()
|
||||
{
|
||||
if (shader_program_ || shader_compile_attempted_) {
|
||||
return;
|
||||
}
|
||||
|
||||
shader_compile_attempted_ = true;
|
||||
|
||||
shader_program_ = compile_shader_program();
|
||||
if (!shader_program_) {
|
||||
return;
|
||||
}
|
||||
|
||||
glUseProgram(shader_program_);
|
||||
|
||||
image_texture_location_ = glGetUniformLocation(shader_program_, "image_texture");
|
||||
if (image_texture_location_ < 0) {
|
||||
LOG_ERROR << "Shader doesn't contain the 'image_texture' uniform.";
|
||||
destroy_shader();
|
||||
return;
|
||||
}
|
||||
|
||||
fullscreen_location_ = glGetUniformLocation(shader_program_, "fullscreen");
|
||||
if (fullscreen_location_ < 0) {
|
||||
LOG_ERROR << "Shader doesn't contain the 'fullscreen' uniform.";
|
||||
destroy_shader();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLShader::destroy_shader()
|
||||
{
|
||||
glDeleteProgram(shader_program_);
|
||||
shader_program_ = 0;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
46
blender-5.2.0/intern/cycles/app/opengl/shader.h
Normal file
46
blender-5.2.0/intern/cycles/app/opengl/shader.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 OpenGL Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/types.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class OpenGLShader {
|
||||
public:
|
||||
static constexpr const char *position_attribute_name = "pos";
|
||||
static constexpr const char *tex_coord_attribute_name = "texCoord";
|
||||
|
||||
OpenGLShader() = default;
|
||||
virtual ~OpenGLShader() = default;
|
||||
|
||||
/* Get attribute location for position and texture coordinate respectively.
|
||||
* NOTE: The shader needs to be bound to have access to those. */
|
||||
int get_position_attrib_location();
|
||||
int get_tex_coord_attrib_location();
|
||||
|
||||
void bind(const int width, const int height);
|
||||
void unbind();
|
||||
|
||||
protected:
|
||||
uint get_shader_program();
|
||||
|
||||
void create_shader_if_needed();
|
||||
void destroy_shader();
|
||||
|
||||
/* Cached values of various OpenGL resources. */
|
||||
int position_attribute_location_ = -1;
|
||||
int tex_coord_attribute_location_ = -1;
|
||||
|
||||
uint shader_program_ = 0;
|
||||
int image_texture_location_ = -1;
|
||||
int fullscreen_location_ = -1;
|
||||
|
||||
/* Shader compilation attempted. Which means, that if the shader program is 0 then compilation or
|
||||
* linking has failed. Do not attempt to re-compile the shader. */
|
||||
bool shader_compile_attempted_ = false;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
350
blender-5.2.0/intern/cycles/app/opengl/window.cpp
Normal file
350
blender-5.2.0/intern/cycles/app/opengl/window.cpp
Normal file
@@ -0,0 +1,350 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "app/opengl/window.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/string.h"
|
||||
#include "util/thread.h"
|
||||
#include "util/version.h"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* structs */
|
||||
|
||||
struct Window {
|
||||
WindowInitFunc initf = nullptr;
|
||||
WindowExitFunc exitf = nullptr;
|
||||
WindowResizeFunc resize = nullptr;
|
||||
WindowDisplayFunc display = nullptr;
|
||||
WindowKeyboardFunc keyboard = nullptr;
|
||||
WindowMotionFunc motion = nullptr;
|
||||
|
||||
bool first_display = true;
|
||||
bool redraw = false;
|
||||
|
||||
int mouseX = 0, mouseY = 0;
|
||||
int mouseBut0 = 0, mouseBut2 = 0;
|
||||
|
||||
int width = 0, height = 0;
|
||||
|
||||
SDL_Window *window = nullptr;
|
||||
SDL_GLContext gl_context = nullptr;
|
||||
thread_mutex gl_context_mutex;
|
||||
} V;
|
||||
|
||||
/* public */
|
||||
|
||||
static void window_display_text(int /*x*/, int /*y*/, const char *text)
|
||||
{
|
||||
/* Not currently supported, need to add text rendering support. */
|
||||
#if 0
|
||||
const char *c;
|
||||
|
||||
glRasterPos3f(x, y, 0);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
|
||||
printf("display %s\n", text);
|
||||
|
||||
for (c = text; *c != '\0'; c++) {
|
||||
const uint8_t *bitmap = helvetica10_character_map[*c];
|
||||
glBitmap(bitmap[0],
|
||||
helvetica10_height,
|
||||
helvetica10_x_offset,
|
||||
helvetica10_y_offset,
|
||||
bitmap[0],
|
||||
0.0f,
|
||||
bitmap + 1);
|
||||
}
|
||||
#else
|
||||
static string last_text;
|
||||
|
||||
if (text != last_text) {
|
||||
LOG_INFO_IMPORTANT << text;
|
||||
last_text = text;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void window_display_info(const char *info)
|
||||
{
|
||||
const int height = 20;
|
||||
|
||||
#if 0
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glColor4f(0.1f, 0.1f, 0.1f, 0.8f);
|
||||
glRectf(0.0f, V.height - height, V.width, V.height);
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
glColor3f(0.5f, 0.5f, 0.5f);
|
||||
#endif
|
||||
|
||||
window_display_text(10, 7 + V.height - height, info);
|
||||
|
||||
#if 0
|
||||
glColor3f(1.0f, 1.0f, 1.0f);
|
||||
#endif
|
||||
}
|
||||
|
||||
void window_display_help()
|
||||
{
|
||||
const int w = (int)((float)V.width / 1.15f);
|
||||
const int h = (int)((float)V.height / 1.15f);
|
||||
|
||||
const int x1 = (V.width - w) / 2;
|
||||
#if 0
|
||||
const int x2 = x1 + w;
|
||||
#endif
|
||||
|
||||
const int y1 = (V.height - h) / 2;
|
||||
const int y2 = y1 + h;
|
||||
|
||||
#if 0
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glColor4f(0.5f, 0.5f, 0.5f, 0.8f);
|
||||
glRectf(x1, y1, x2, y2);
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
glColor3f(0.8f, 0.8f, 0.8f);
|
||||
#endif
|
||||
|
||||
const string info = string("Cycles Renderer ") + CYCLES_VERSION_STRING;
|
||||
|
||||
window_display_text(x1 + 20, y2 - 20, info.c_str());
|
||||
window_display_text(x1 + 20, y2 - 40, "(C) 2011-2016 Blender Foundation");
|
||||
window_display_text(x1 + 20, y2 - 80, "Controls:");
|
||||
window_display_text(x1 + 20, y2 - 100, "h: Info/Help");
|
||||
window_display_text(x1 + 20, y2 - 120, "r: Reset");
|
||||
window_display_text(x1 + 20, y2 - 140, "p: Pause");
|
||||
window_display_text(x1 + 20, y2 - 160, "esc: Cancel");
|
||||
window_display_text(x1 + 20, y2 - 180, "q: Quit program");
|
||||
|
||||
window_display_text(x1 + 20, y2 - 210, "i: Interactive mode");
|
||||
window_display_text(x1 + 20, y2 - 230, "Left mouse: Move camera");
|
||||
window_display_text(x1 + 20, y2 - 250, "Right mouse: Rotate camera");
|
||||
window_display_text(x1 + 20, y2 - 270, "W/A/S/D: Move camera");
|
||||
window_display_text(x1 + 20, y2 - 290, "0/1/2/3: Set max bounces");
|
||||
|
||||
#if 0
|
||||
glColor3f(1.0f, 1.0f, 1.0f);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void window_display()
|
||||
{
|
||||
if (V.first_display) {
|
||||
if (V.initf) {
|
||||
V.initf();
|
||||
}
|
||||
if (V.exitf) {
|
||||
atexit(V.exitf);
|
||||
}
|
||||
|
||||
V.first_display = false;
|
||||
}
|
||||
|
||||
window_opengl_context_enable();
|
||||
|
||||
glViewport(0, 0, V.width, V.height);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
glClearColor(0.05f, 0.05f, 0.05f, 0.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glOrtho(0, V.width, 0, V.height, -1, 1);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
glRasterPos3f(0, 0, 0);
|
||||
|
||||
if (V.display) {
|
||||
V.display();
|
||||
}
|
||||
|
||||
SDL_GL_SwapWindow(V.window);
|
||||
window_opengl_context_disable();
|
||||
}
|
||||
|
||||
static void window_reshape(const int width, const int height)
|
||||
{
|
||||
if (V.width != width || V.height != height) {
|
||||
if (V.resize) {
|
||||
V.resize(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
V.width = width;
|
||||
V.height = height;
|
||||
}
|
||||
|
||||
static bool window_keyboard(unsigned char key)
|
||||
{
|
||||
if (V.keyboard) {
|
||||
V.keyboard(key);
|
||||
}
|
||||
|
||||
if (key == 'q') {
|
||||
if (V.exitf) {
|
||||
V.exitf();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void window_mouse(const int button, const int state, const int x, int y)
|
||||
{
|
||||
if (button == SDL_BUTTON_LEFT) {
|
||||
if (state == SDL_EVENT_MOUSE_BUTTON_DOWN) {
|
||||
V.mouseX = x;
|
||||
V.mouseY = y;
|
||||
V.mouseBut0 = 1;
|
||||
}
|
||||
else if (state == SDL_EVENT_MOUSE_BUTTON_UP) {
|
||||
V.mouseBut0 = 0;
|
||||
}
|
||||
}
|
||||
else if (button == SDL_BUTTON_RIGHT) {
|
||||
if (state == SDL_EVENT_MOUSE_BUTTON_DOWN) {
|
||||
V.mouseX = x;
|
||||
V.mouseY = y;
|
||||
V.mouseBut2 = 1;
|
||||
}
|
||||
else if (state == SDL_EVENT_MOUSE_BUTTON_UP) {
|
||||
V.mouseBut2 = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void window_motion(const int x, const int y)
|
||||
{
|
||||
const int but = V.mouseBut0 ? 0 : 2;
|
||||
const int distX = x - V.mouseX;
|
||||
const int distY = y - V.mouseY;
|
||||
|
||||
if (V.motion) {
|
||||
V.motion(distX, distY, but);
|
||||
}
|
||||
|
||||
V.mouseX = x;
|
||||
V.mouseY = y;
|
||||
}
|
||||
|
||||
bool window_opengl_context_enable()
|
||||
{
|
||||
V.gl_context_mutex.lock();
|
||||
SDL_GL_MakeCurrent(V.window, V.gl_context);
|
||||
return true;
|
||||
}
|
||||
|
||||
void window_opengl_context_disable()
|
||||
{
|
||||
SDL_GL_MakeCurrent(V.window, nullptr);
|
||||
V.gl_context_mutex.unlock();
|
||||
}
|
||||
|
||||
void window_main_loop(const char *title,
|
||||
const int width,
|
||||
const int height,
|
||||
WindowInitFunc initf,
|
||||
WindowExitFunc exitf,
|
||||
WindowResizeFunc resize,
|
||||
WindowDisplayFunc display,
|
||||
WindowKeyboardFunc keyboard,
|
||||
WindowMotionFunc motion)
|
||||
{
|
||||
V.width = width;
|
||||
V.height = height;
|
||||
V.first_display = true;
|
||||
V.redraw = false;
|
||||
V.initf = initf;
|
||||
V.exitf = exitf;
|
||||
V.resize = resize;
|
||||
V.display = display;
|
||||
V.keyboard = keyboard;
|
||||
V.motion = motion;
|
||||
|
||||
SDL_Init(SDL_INIT_VIDEO);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
|
||||
V.window = SDL_CreateWindow(title, width, height, SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL);
|
||||
if (V.window == nullptr) {
|
||||
LOG_ERROR << "Failed to create window: " << SDL_GetError();
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_RaiseWindow(V.window);
|
||||
|
||||
V.gl_context = SDL_GL_CreateContext(V.window);
|
||||
SDL_GL_MakeCurrent(V.window, nullptr);
|
||||
|
||||
window_reshape(width, height);
|
||||
window_display();
|
||||
|
||||
while (true) {
|
||||
bool quit = false;
|
||||
SDL_Event event;
|
||||
while (!quit && SDL_PollEvent(&event)) {
|
||||
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key < 128) {
|
||||
quit = window_keyboard(char(event.key.key));
|
||||
}
|
||||
else if (event.type == SDL_EVENT_MOUSE_MOTION) {
|
||||
window_motion(int(event.motion.x), int(event.motion.y));
|
||||
}
|
||||
else if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN ||
|
||||
event.type == SDL_EVENT_MOUSE_BUTTON_UP)
|
||||
{
|
||||
window_mouse(event.button.button, event.type, int(event.button.x), int(event.button.y));
|
||||
}
|
||||
else if (event.type == SDL_EVENT_WINDOW_RESIZED) {
|
||||
window_reshape(event.window.data1, event.window.data2);
|
||||
}
|
||||
else if (event.type == SDL_EVENT_QUIT) {
|
||||
if (V.exitf) {
|
||||
V.exitf();
|
||||
}
|
||||
quit = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (quit) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (V.redraw) {
|
||||
V.redraw = false;
|
||||
window_display();
|
||||
}
|
||||
|
||||
SDL_WaitEventTimeout(nullptr, 100);
|
||||
}
|
||||
|
||||
SDL_GL_DestroyContext(V.gl_context);
|
||||
SDL_DestroyWindow(V.window);
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
void window_redraw()
|
||||
{
|
||||
V.redraw = true;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
36
blender-5.2.0/intern/cycles/app/opengl/window.h
Normal file
36
blender-5.2.0/intern/cycles/app/opengl/window.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
/* Functions to display a simple OpenGL window using SDL, simplified to the
|
||||
* bare minimum we need to reduce boilerplate code in tests apps. */
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
using WindowInitFunc = void (*)();
|
||||
using WindowExitFunc = void (*)();
|
||||
using WindowResizeFunc = void (*)(int, int);
|
||||
using WindowDisplayFunc = void (*)();
|
||||
using WindowKeyboardFunc = void (*)(unsigned char);
|
||||
using WindowMotionFunc = void (*)(int, int, int);
|
||||
|
||||
void window_main_loop(const char *title,
|
||||
const int width,
|
||||
const int height,
|
||||
WindowInitFunc initf,
|
||||
WindowExitFunc exitf,
|
||||
WindowResizeFunc resize,
|
||||
WindowDisplayFunc display,
|
||||
WindowKeyboardFunc keyboard,
|
||||
WindowMotionFunc motion);
|
||||
|
||||
void window_display_info(const char *info);
|
||||
void window_display_help();
|
||||
void window_redraw();
|
||||
|
||||
bool window_opengl_context_enable();
|
||||
void window_opengl_context_disable();
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
Reference in New Issue
Block a user