forked from bartvdbraak/blender
Movies support for Cycles
This adds support of movie textures for Cycles rendering. Uses the same builtin images routines as packed/generated images, but with some extra non-rna hookups from blender_session side. Basically, it's not so clear how to give access to video frames via C++ RNA -- it'll require exposing ImBuf to API, doing some threading locks and so. Ended up adding two more functions which are actually bad level call, but don't consider it's so much bad -- we have few bad calls already, which are actually related. Changed a bit how builtin images names are passing to image manager. Now it's not just an ID datablock name, but also a frame number concatenated via '@' character, which makes itpossible to easily know frame number to be used for movie images, without adding extra descriptors to image manager. Decoding of builtin name is a bit slower now, but it should be still nothing in comparison with rendering complexity. Also exposed image user's frame_current to python API, which is needed to get absolute frame number of movie from node's image user. P.S. Generated/packed images are also using bad level call but only does it to make things more clear here. Either all images are using C++ RNA here or no images does. That's the most clear for now.
This commit is contained in:
parent
f55a9eb3d4
commit
b01233b07f
@ -612,8 +612,25 @@ void BlenderSession::test_cancel()
|
||||
session->progress.set_cancel("Cancelled");
|
||||
}
|
||||
|
||||
void BlenderSession::builtin_image_info(const string &name, bool &is_float, int &width, int &height, int &channels)
|
||||
/* builtin image file name is actually an image datablock name with
|
||||
* absolute sequence frame number concatenated via '@' character
|
||||
*
|
||||
* this function splits image id name and frame number from a
|
||||
* builtin image name
|
||||
*/
|
||||
void BlenderSession::builtin_name_split(const string &builtin_name, string &name, int &frame)
|
||||
{
|
||||
int last = builtin_name.find_last_of('@');
|
||||
name = builtin_name.substr(0, last);
|
||||
frame = atoi(builtin_name.substr(last + 1, builtin_name.size() - last - 1).c_str());
|
||||
}
|
||||
|
||||
void BlenderSession::builtin_image_info(const string &builtin_name, bool &is_float, int &width, int &height, int &channels)
|
||||
{
|
||||
string name;
|
||||
int frame;
|
||||
builtin_name_split(builtin_name, name, frame);
|
||||
|
||||
BL::Image b_image = b_data.images[name];
|
||||
|
||||
if(b_image) {
|
||||
@ -630,8 +647,12 @@ void BlenderSession::builtin_image_info(const string &name, bool &is_float, int
|
||||
}
|
||||
}
|
||||
|
||||
bool BlenderSession::builtin_image_pixels(const string &name, unsigned char *pixels)
|
||||
bool BlenderSession::builtin_image_pixels(const string &builtin_name, unsigned char *pixels)
|
||||
{
|
||||
string name;
|
||||
int frame;
|
||||
builtin_name_split(builtin_name, name, frame);
|
||||
|
||||
BL::Image b_image = b_data.images[name];
|
||||
|
||||
if(b_image) {
|
||||
@ -639,16 +660,27 @@ bool BlenderSession::builtin_image_pixels(const string &name, unsigned char *pix
|
||||
int height = b_image.size()[1];
|
||||
int channels = b_image.channels();
|
||||
|
||||
BL::DynamicArray<float> pixels_array = b_image.pixels();
|
||||
float *float_pixels = pixels_array.data;
|
||||
unsigned char *image_pixels;
|
||||
image_pixels = image_get_pixels_for_frame(b_image, frame);
|
||||
|
||||
/* a bit of shame, but Py API currently only returns float array,
|
||||
* which need to be converted back to char buffer
|
||||
*/
|
||||
unsigned char *cp = pixels;
|
||||
float *fp = float_pixels;
|
||||
for(int i = 0; i < channels * width * height; i++, cp++, fp++) {
|
||||
*cp = *fp * 255;
|
||||
if(image_pixels) {
|
||||
memcpy(pixels, image_pixels, width * height * channels * sizeof(unsigned char));
|
||||
MEM_freeN(image_pixels);
|
||||
}
|
||||
else {
|
||||
if(channels == 1) {
|
||||
memset(pixels, 0, width * height * sizeof(unsigned char));
|
||||
}
|
||||
else {
|
||||
unsigned char *cp = pixels;
|
||||
for(int i = 0; i < width * height; i++, cp += channels) {
|
||||
cp[0] = 255;
|
||||
cp[1] = 0;
|
||||
cp[2] = 255;
|
||||
if(channels == 4)
|
||||
cp[3] = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -657,8 +689,12 @@ bool BlenderSession::builtin_image_pixels(const string &name, unsigned char *pix
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BlenderSession::builtin_image_float_pixels(const string &name, float *pixels)
|
||||
bool BlenderSession::builtin_image_float_pixels(const string &builtin_name, float *pixels)
|
||||
{
|
||||
string name;
|
||||
int frame;
|
||||
builtin_name_split(builtin_name, name, frame);
|
||||
|
||||
BL::Image b_image = b_data.images[name];
|
||||
|
||||
if(b_image) {
|
||||
@ -666,9 +702,28 @@ bool BlenderSession::builtin_image_float_pixels(const string &name, float *pixel
|
||||
int height = b_image.size()[1];
|
||||
int channels = b_image.channels();
|
||||
|
||||
BL::DynamicArray<float> pixels_array = b_image.pixels();
|
||||
float *image_pixels;
|
||||
image_pixels = image_get_float_pixels_for_frame(b_image, frame);
|
||||
|
||||
memcpy(pixels, pixels_array.data, width * height * channels * sizeof(float));
|
||||
if(image_pixels) {
|
||||
memcpy(pixels, image_pixels, width * height * channels * sizeof(float));
|
||||
MEM_freeN(image_pixels);
|
||||
}
|
||||
else {
|
||||
if(channels == 1) {
|
||||
memset(pixels, 0, width * height * sizeof(float));
|
||||
}
|
||||
else {
|
||||
float *fp = pixels;
|
||||
for(int i = 0; i < width * height; i++, fp += channels) {
|
||||
fp[0] = 1.0f;
|
||||
fp[1] = 0.0f;
|
||||
fp[2] = 1.0f;
|
||||
if(channels == 4)
|
||||
fp[3] = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -94,9 +94,10 @@ protected:
|
||||
void do_write_update_render_result(BL::RenderResult b_rr, BL::RenderLayer b_rlay, RenderTile& rtile, bool do_update_only);
|
||||
void do_write_update_render_tile(RenderTile& rtile, bool do_update_only);
|
||||
|
||||
void builtin_image_info(const string &name, bool &is_float, int &width, int &height, int &channels);
|
||||
bool builtin_image_pixels(const string &name, unsigned char *pixels);
|
||||
bool builtin_image_float_pixels(const string &name, float *pixels);
|
||||
void builtin_name_split(const string &builtin_name, string &name, int &frame);
|
||||
void builtin_image_info(const string &builtin_name, bool &is_float, int &width, int &height, int &channels);
|
||||
bool builtin_image_pixels(const string &builtin_name, unsigned char *pixels);
|
||||
bool builtin_image_float_pixels(const string &builtin_name, float *pixels);
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
@ -511,17 +511,24 @@ static ShaderNode *add_node(Scene *scene, BL::BlendData b_data, BL::Scene b_scen
|
||||
BL::ShaderNodeTexImage b_image_node(b_node);
|
||||
BL::Image b_image(b_image_node.image());
|
||||
ImageTextureNode *image = new ImageTextureNode();
|
||||
/* todo: handle movie images */
|
||||
if(b_image && b_image.source() != BL::Image::source_MOVIE) {
|
||||
if(b_image) {
|
||||
/* builtin images will use callback-based reading because
|
||||
* they could only be loaded correct from blender side
|
||||
*/
|
||||
bool is_builtin = b_image.packed_file() ||
|
||||
b_image.source() == BL::Image::source_GENERATED;
|
||||
b_image.source() == BL::Image::source_GENERATED ||
|
||||
b_image.source() == BL::Image::source_MOVIE;
|
||||
|
||||
if(is_builtin) {
|
||||
/* for builtin images we're using image datablock name to find an image to read pixels from later */
|
||||
image->filename = b_image.name();
|
||||
/* for builtin images we're using image datablock name to find an image to
|
||||
* read pixels from later
|
||||
*
|
||||
* also store frame number as well, so there's no differences in handling
|
||||
* builtin names for packed images and movies
|
||||
*/
|
||||
int scene_frame = b_scene.frame_current();
|
||||
int image_frame = image_user_frame_number(b_image_node.image_user(), scene_frame);
|
||||
image->filename = b_image.name() + "@" + string_printf("%d", image_frame);
|
||||
image->is_builtin = true;
|
||||
}
|
||||
else {
|
||||
@ -542,12 +549,15 @@ static ShaderNode *add_node(Scene *scene, BL::BlendData b_data, BL::Scene b_scen
|
||||
BL::ShaderNodeTexEnvironment b_env_node(b_node);
|
||||
BL::Image b_image(b_env_node.image());
|
||||
EnvironmentTextureNode *env = new EnvironmentTextureNode();
|
||||
if(b_image && b_image.source() != BL::Image::source_MOVIE) {
|
||||
if(b_image) {
|
||||
bool is_builtin = b_image.packed_file() ||
|
||||
b_image.source() == BL::Image::source_GENERATED;
|
||||
b_image.source() == BL::Image::source_GENERATED ||
|
||||
b_image.source() == BL::Image::source_MOVIE;
|
||||
|
||||
if(is_builtin) {
|
||||
env->filename = b_image.name();
|
||||
int scene_frame = b_scene.frame_current();
|
||||
int image_frame = image_user_frame_number(b_env_node.image_user(), scene_frame);
|
||||
env->filename = b_image.name() + "@" + string_printf("%d", image_frame);
|
||||
env->is_builtin = true;
|
||||
}
|
||||
else {
|
||||
|
@ -33,6 +33,8 @@ extern "C" {
|
||||
void BLI_timestr(double _time, char *str);
|
||||
void BKE_image_user_frame_calc(void *iuser, int cfra, int fieldnr);
|
||||
void BKE_image_user_file_path(void *iuser, void *ima, char *path);
|
||||
unsigned char *BKE_image_get_pixels_for_frame(void *image, int frame);
|
||||
float *BKE_image_get_float_pixels_for_frame(void *image, int frame);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
@ -100,6 +102,22 @@ static inline string image_user_file_path(BL::ImageUser iuser, BL::Image ima, in
|
||||
return string(filepath);
|
||||
}
|
||||
|
||||
static inline int image_user_frame_number(BL::ImageUser iuser, int cfra)
|
||||
{
|
||||
BKE_image_user_frame_calc(iuser.ptr.data, cfra, 0);
|
||||
return iuser.frame_current();
|
||||
}
|
||||
|
||||
static inline unsigned char *image_get_pixels_for_frame(BL::Image image, int frame)
|
||||
{
|
||||
return BKE_image_get_pixels_for_frame(image.ptr.data, frame);
|
||||
}
|
||||
|
||||
static inline float *image_get_float_pixels_for_frame(BL::Image image, int frame)
|
||||
{
|
||||
return BKE_image_get_float_pixels_for_frame(image.ptr.data, frame);
|
||||
}
|
||||
|
||||
/* Utilities */
|
||||
|
||||
static inline Transform get_transform(BL::Array<float, 16> array)
|
||||
|
@ -219,6 +219,10 @@ void BKE_image_buf_fill_color(unsigned char *rect, float *rect_float, int width,
|
||||
void BKE_image_buf_fill_checker(unsigned char *rect, float *rect_float, int height, int width);
|
||||
void BKE_image_buf_fill_checker_color(unsigned char *rect, float *rect_float, int height, int width);
|
||||
|
||||
/* Cycles hookup */
|
||||
unsigned char *BKE_image_get_pixels_for_frame(struct Image *image, int frame);
|
||||
float *BKE_image_get_float_pixels_for_frame(struct Image *image, int frame);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -3199,3 +3199,57 @@ void BKE_image_get_aspect(Image *image, float *aspx, float *aspy)
|
||||
else
|
||||
*aspy = 1.0f;
|
||||
}
|
||||
|
||||
unsigned char *BKE_image_get_pixels_for_frame(struct Image *image, int frame)
|
||||
{
|
||||
ImageUser iuser = {0};
|
||||
void *lock;
|
||||
ImBuf *ibuf;
|
||||
unsigned char *pixels = NULL;
|
||||
|
||||
iuser.framenr = frame;
|
||||
iuser.ok = TRUE;
|
||||
|
||||
ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock);
|
||||
|
||||
if (ibuf) {
|
||||
pixels = (unsigned char *) ibuf->rect;
|
||||
|
||||
if (pixels)
|
||||
pixels = MEM_dupallocN(pixels);
|
||||
|
||||
BKE_image_release_ibuf(image, ibuf, lock);
|
||||
}
|
||||
|
||||
if (!pixels)
|
||||
return NULL;
|
||||
|
||||
return pixels;
|
||||
}
|
||||
|
||||
float *BKE_image_get_float_pixels_for_frame(struct Image *image, int frame)
|
||||
{
|
||||
ImageUser iuser = {0};
|
||||
void *lock;
|
||||
ImBuf *ibuf;
|
||||
float *pixels = NULL;
|
||||
|
||||
iuser.framenr = frame;
|
||||
iuser.ok = TRUE;
|
||||
|
||||
ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock);
|
||||
|
||||
if (ibuf) {
|
||||
pixels = ibuf->rect_float;
|
||||
|
||||
if (pixels)
|
||||
pixels = MEM_dupallocN(pixels);
|
||||
|
||||
BKE_image_release_ibuf(image, ibuf, lock);
|
||||
}
|
||||
|
||||
if (!pixels)
|
||||
return NULL;
|
||||
|
||||
return pixels;
|
||||
}
|
||||
|
@ -427,6 +427,11 @@ static void rna_def_imageuser(BlenderRNA *brna)
|
||||
RNA_def_property_update(prop, 0, "rna_ImageUser_update");
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
|
||||
prop = RNA_def_property(srna, "frame_current", PROP_INT, PROP_TIME);
|
||||
RNA_def_property_int_sdna(prop, NULL, "framenr");
|
||||
RNA_def_property_range(prop, MINAFRAME, MAXFRAME);
|
||||
RNA_def_property_ui_text(prop, "Current Frame", "Current frame number in image sequence or movie");
|
||||
|
||||
/* animation */
|
||||
prop = RNA_def_property(srna, "use_cyclic", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "cycl", 0);
|
||||
|
Loading…
Reference in New Issue
Block a user