| /* | |
| * GPAC - Multimedia Framework C SDK | |
| * | |
| * Authors: Jean Le Feuvre | |
| * Copyright (c) Telecom ParisTech 2000-2023 | |
| * All rights reserved | |
| * | |
| * This file is part of GPAC / Scene Compositor sub-project | |
| * | |
| * GPAC is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU Lesser General Public License as published by | |
| * the Free Software Foundation; either version 2, or (at your option) | |
| * any later version. | |
| * | |
| * GPAC is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; see the file COPYING. If not, write to | |
| * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
| * | |
| */ | |
| #include "visual_manager.h" | |
| #include "texturing.h" | |
| #include "nodes_stacks.h" | |
| #if !defined(GPAC_DISABLE_3D) && !defined(GPAC_DISABLE_COMPOSITOR) | |
| #ifdef GPAC_USE_TINYGL | |
| #include <GL/oscontext.h> | |
| #endif | |
| GF_Err compositor_3d_set_aspect_ratio(GF_Compositor *compositor) | |
| { | |
| GF_Event evt; | |
| Double ratio; | |
| Fixed scaleX, scaleY; | |
| if (!compositor->display_height || !compositor->display_width) return GF_OK; | |
| compositor->visual->camera.flags |= CAM_IS_DIRTY; | |
| compositor->output_width = compositor->vp_width = compositor->display_width; | |
| compositor->output_height = compositor->vp_height = compositor->display_height; | |
| compositor->vp_x = 0; | |
| compositor->vp_y = 0; | |
| scaleX = scaleY = FIX_ONE; | |
| if (!compositor->has_size_info || compositor->inherit_type_3d) { | |
| compositor->visual->width = compositor->vp_width; | |
| compositor->visual->height = compositor->vp_height; | |
| } else { | |
| switch (compositor->aspect_ratio) { | |
| case GF_ASPECT_RATIO_FILL_SCREEN: | |
| break; | |
| case GF_ASPECT_RATIO_16_9: | |
| compositor->vp_height = 9 * compositor->vp_width / 16; | |
| break; | |
| case GF_ASPECT_RATIO_4_3: | |
| compositor->vp_height = 3 * compositor->vp_width / 4; | |
| break; | |
| default: | |
| ratio = compositor->scene_height; | |
| ratio /= compositor->scene_width; | |
| if (compositor->vp_width * ratio > compositor->vp_height) { | |
| compositor->vp_width = compositor->vp_height * compositor->scene_width; | |
| compositor->vp_width /= compositor->scene_height; | |
| } | |
| else { | |
| compositor->vp_height = compositor->vp_width * compositor->scene_height; | |
| compositor->vp_height /= compositor->scene_width; | |
| } | |
| break; | |
| } | |
| compositor->vp_x = (compositor->display_width - compositor->vp_width) / 2; | |
| compositor->vp_y = (compositor->display_height - compositor->vp_height) / 2; | |
| /*update size info for main visual*/ | |
| if (compositor->visual) { | |
| compositor->visual->width = compositor->scene_width; | |
| compositor->visual->height = compositor->scene_height; | |
| } | |
| /*scaling is still needed for bitmap*/ | |
| scaleX = gf_divfix(INT2FIX(compositor->vp_width), INT2FIX(compositor->scene_width)); | |
| scaleY = gf_divfix(INT2FIX(compositor->vp_height), INT2FIX(compositor->scene_height)); | |
| } | |
| if (compositor->has_size_info) { | |
| compositor->traverse_state->vp_size.x = INT2FIX(compositor->scene_width); | |
| compositor->traverse_state->vp_size.y = INT2FIX(compositor->scene_height); | |
| } else { | |
| compositor->traverse_state->vp_size.x = INT2FIX(compositor->output_width); | |
| compositor->traverse_state->vp_size.y = INT2FIX(compositor->output_height); | |
| } | |
| compositor_set_ar_scale(compositor, scaleX, scaleY); | |
| /*and resetup OpenGL*/ | |
| evt.type = GF_EVENT_VIDEO_SETUP; | |
| evt.setup.width = compositor->display_width; | |
| evt.setup.height = compositor->display_height; | |
| evt.setup.back_buffer = GF_TRUE; | |
| evt.setup.disable_vsync = compositor->bench_mode ? GF_TRUE : GF_FALSE; | |
| #ifdef GPAC_USE_TINYGL | |
| evt.setup.use_opengl = GF_FALSE; | |
| #else | |
| evt.setup.use_opengl = GF_TRUE; | |
| compositor->is_opengl = GF_TRUE; | |
| #endif | |
| if (compositor->video_out->ProcessEvent(compositor->video_out, &evt)<0) { | |
| gf_sc_reset_graphics(compositor); | |
| return GF_OK; | |
| } | |
| if (evt.setup.use_opengl) { | |
| gf_opengl_init(); | |
| } | |
| #if defined(GPAC_USE_TINYGL) | |
| { | |
| u32 bpp; | |
| GF_VideoSurface bb; | |
| GF_Err e = compositor->video_out->LockBackBuffer(compositor->video_out, &bb, 1); | |
| if (e==GF_OK) { | |
| switch (bb.pixel_format) { | |
| case GF_PIXEL_RGBX: | |
| case GF_PIXEL_ARGB: | |
| bpp = 32; | |
| break; | |
| case GF_PIXEL_RGB: | |
| case GF_PIXEL_BGR: | |
| bpp = 24; | |
| break; | |
| case GF_PIXEL_RGB_565: | |
| case GF_PIXEL_RGB_555: | |
| bpp = 16; | |
| break; | |
| default: | |
| e = GF_NOT_SUPPORTED; | |
| bpp = 0; | |
| break; | |
| } | |
| if (e==GF_OK) { | |
| compositor->tgl_ctx = ostgl_create_context(bb.width, bb.height, bpp, &bb.video_buffer, 1); | |
| if (compositor->tgl_ctx) ostgl_make_current(compositor->tgl_ctx, 0); | |
| } | |
| compositor->video_out->LockBackBuffer(compositor->video_out, &bb, 0); | |
| } | |
| } | |
| #endif | |
| return GF_OK; | |
| } | |
| GF_Camera *compositor_3d_get_camera(GF_Compositor *compositor) | |
| { | |
| #ifndef GPAC_DISABLE_VRML | |
| if (compositor->active_layer) { | |
| return compositor_layer3d_get_camera(compositor->active_layer); | |
| } | |
| #endif | |
| if (compositor->visual->type_3d) | |
| return &compositor->visual->camera; | |
| return NULL; | |
| } | |
| void compositor_3d_reset_camera(GF_Compositor *compositor) | |
| { | |
| GF_Camera *cam = compositor_3d_get_camera(compositor); | |
| if (cam) { | |
| camera_reset_viewpoint(cam, GF_TRUE); | |
| gf_sc_invalidate(compositor, NULL); | |
| } | |
| if (compositor->active_layer) gf_node_dirty_set(compositor->active_layer, 0, GF_TRUE); | |
| } | |
| void compositor_3d_draw_bitmap(Drawable *stack, DrawAspect2D *asp, GF_TraverseState *tr_state, Fixed width, Fixed height, Fixed bmp_scale_x, Fixed bmp_scale_y) | |
| { | |
| u8 alpha; | |
| GF_TextureHandler *txh; | |
| GF_Compositor *compositor = tr_state->visual->compositor; | |
| if (!asp->fill_texture) | |
| return; | |
| txh = asp->fill_texture; | |
| if (!txh || !txh->tx_io || !txh->width || !txh->height) | |
| return; | |
| if (((txh->pixelformat==GF_PIXEL_RGBD) || (txh->pixelformat==GF_PIXEL_YUVD))) { | |
| if (compositor->depth_gl_type) { | |
| if (txh->data && gf_sc_texture_convert(txh) ) | |
| visual_3d_point_sprite(tr_state->visual, stack, txh, tr_state); | |
| return; | |
| } | |
| } | |
| alpha = GF_COL_A(asp->fill_color); | |
| /*THIS IS A HACK, will not work when setting filled=0, transparency and XLineProps*/ | |
| if (!alpha) alpha = GF_COL_A(asp->line_color); | |
| visual_3d_set_state(tr_state->visual, V3D_STATE_LIGHT, GF_FALSE); | |
| visual_3d_enable_antialias(tr_state->visual, GF_FALSE); | |
| visual_3d_set_material_2d_argb(tr_state->visual, GF_COL_ARGB(alpha, 0xFF, 0xFF, 0xFF)); | |
| if (alpha && (alpha != 0xFF)) { | |
| gf_sc_texture_set_blend_mode(txh, TX_MODULATE); | |
| } else if (gf_sc_texture_is_transparent(txh)) { | |
| gf_sc_texture_set_blend_mode(txh, TX_REPLACE); | |
| } else { | |
| visual_3d_set_state(tr_state->visual, V3D_STATE_BLEND, GF_FALSE); | |
| } | |
| /*ignore texture transform for bitmap*/ | |
| tr_state->mesh_num_textures = gf_sc_texture_enable(txh, NULL); | |
| if (tr_state->mesh_num_textures) { | |
| /*we must check the w & h passed are correct because of bitmap node initialization*/ | |
| if (width && height) { | |
| if (!stack->mesh) { | |
| SFVec2f size; | |
| size.x = width; | |
| size.y = height; | |
| stack->mesh = new_mesh(); | |
| if (txh->stream) { | |
| const GF_PropertyValue *srd = NULL; | |
| const GF_PropertyValue *srd_ref = NULL; | |
| if (txh->stream->odm && txh->stream->odm->pid) { | |
| srd = gf_filter_pid_get_property(txh->stream->odm->pid, GF_PROP_PID_SRD_MAP); | |
| srd_ref = gf_filter_pid_get_property(txh->stream->odm->pid, GF_PROP_PID_SRD_REF); | |
| } | |
| if (srd && srd_ref) { | |
| void mesh_new_planar_srd(GF_Mesh *mesh, SFVec2f size, u32 flip, u32 rotate, const GF_PropertyValue *srd_map, const GF_PropertyValue *srd_ref); | |
| mesh_new_planar_srd(stack->mesh, size, txh->stream->flip, txh->stream->rotate, srd, srd_ref); | |
| } else { | |
| mesh_new_rectangle_ex(stack->mesh, size, NULL, txh->stream->flip, txh->stream->rotate); | |
| } | |
| } else { | |
| mesh_new_rectangle(stack->mesh, size, NULL, GF_FALSE); | |
| } | |
| } | |
| } | |
| if (stack->mesh) { | |
| #ifdef GF_SR_USE_DEPTH | |
| if (tr_state->depth_offset) { | |
| GF_Matrix mx; | |
| Fixed offset; | |
| Fixed disp_depth = (compositor->dispdepth<0) ? INT2FIX(tr_state->visual->height) : INT2FIX(compositor->dispdepth); | |
| if (disp_depth) { | |
| GF_Matrix bck_mx; | |
| if (!tr_state->pixel_metrics) disp_depth = gf_divfix(disp_depth, tr_state->min_hsize); | |
| gf_mx_init(mx); | |
| /*add recalibration by the scene*/ | |
| offset = tr_state->depth_offset; | |
| if (tr_state->visual->depth_vp_range) { | |
| offset = gf_divfix(offset, tr_state->visual->depth_vp_range/2); | |
| } | |
| gf_mx_add_translation(&mx, 0, 0, gf_mulfix(offset, disp_depth/2) ); | |
| gf_mx_copy(bck_mx, tr_state->model_matrix); | |
| gf_mx_add_matrix(&tr_state->model_matrix, &mx); | |
| visual_3d_mesh_paint(tr_state, stack->mesh); | |
| gf_mx_copy(tr_state->model_matrix, bck_mx); | |
| } else { | |
| visual_3d_mesh_paint(tr_state, stack->mesh); | |
| } | |
| } else | |
| #endif | |
| visual_3d_mesh_paint(tr_state, stack->mesh); | |
| } | |
| gf_sc_texture_disable(txh); | |
| tr_state->mesh_num_textures = 0; | |
| } | |
| } | |
| #endif //!defined(GPAC_DISABLE_3D) && !defined(GPAC_DISABLE_COMPOSITOR) |