| /* | |
| * GPAC - Multimedia Framework C SDK | |
| * | |
| * Authors: Jean Le Feuvre | |
| * Copyright (c) Telecom ParisTech 2000-2026 | |
| * 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 "nodes_stacks.h" | |
| #include <gpac/nodes_mpeg4.h> | |
| #include <gpac/nodes_x3d.h> | |
| #if !defined(GPAC_DISABLE_VRML) && !defined(GPAC_DISABLE_COMPOSITOR) | |
| typedef struct | |
| { | |
| GF_TimeNode time_handle; | |
| Bool store_info; | |
| Double start_time, cycle_interval; | |
| u32 num_cycles; | |
| GF_Compositor *compositor; | |
| #ifndef GPAC_DISABLE_X3D | |
| Bool is_x3d; | |
| #endif | |
| } TimeSensorStack; | |
| static void timesensor_destroy(GF_Node *ts, void *rs, Bool is_destroy) | |
| { | |
| if (is_destroy) { | |
| TimeSensorStack *st = (TimeSensorStack *)gf_node_get_private(ts); | |
| if (st->time_handle.is_registered) { | |
| gf_sc_unregister_time_node(st->compositor, &st->time_handle); | |
| } | |
| gf_free(st); | |
| } | |
| } | |
| static | |
| void timesensor_deactivate(TimeSensorStack *stack, M_TimeSensor *ts) | |
| { | |
| ts->isActive = 0; | |
| gf_node_event_out((GF_Node *) ts, 7);//"isActive" | |
| gf_assert(stack->time_handle.is_registered); | |
| stack->time_handle.needs_unregister = 1; | |
| stack->num_cycles = 0; | |
| } | |
| static | |
| void timesensor_update_time(GF_TimeNode *st) | |
| { | |
| Double currentTime, cycleTime; | |
| Fixed newFraction; | |
| u32 inc; | |
| M_TimeSensor *TS = (M_TimeSensor *)st->udta; | |
| TimeSensorStack *stack = (TimeSensorStack *)gf_node_get_private(st->udta); | |
| if (! TS->enabled) { | |
| if (TS->isActive) { | |
| TS->cycleTime = gf_node_get_scene_time(st->udta); | |
| gf_node_event_out(st->udta, 5);//"cycleTime" | |
| timesensor_deactivate(stack, TS); | |
| } | |
| return; | |
| } | |
| if (stack->store_info) { | |
| stack->store_info = 0; | |
| stack->start_time = TS->startTime; | |
| stack->cycle_interval = TS->cycleInterval; | |
| } | |
| currentTime = gf_node_get_scene_time(st->udta); | |
| if (!TS->isActive) { | |
| if (currentTime < stack->start_time) return; | |
| /*special case: if we're greater than both start and stop time don't activate*/ | |
| if (!TS->isActive && (TS->stopTime > stack->start_time) && (currentTime >= TS->stopTime)) { | |
| stack->time_handle.needs_unregister = 1; | |
| return; | |
| } | |
| #ifndef GPAC_DISABLE_X3D | |
| if (stack->is_x3d && !TS->loop) { | |
| if (!stack->start_time) return; | |
| if (currentTime >= TS->startTime+stack->cycle_interval) return; | |
| } | |
| #endif | |
| } | |
| cycleTime = currentTime - stack->start_time - stack->num_cycles * stack->cycle_interval; | |
| newFraction = FLT2FIX ( fmod(cycleTime, stack->cycle_interval) / stack->cycle_interval ); | |
| if (TS->isActive) { | |
| TS->time = currentTime; | |
| gf_node_event_out(st->udta, 8);//"time" | |
| /*VRML: | |
| "f = fmod( (now - startTime) , cycleInterval) / cycleInterval | |
| if (f == 0.0 && now > startTime) fraction_changed = 1.0 | |
| else fraction_changed = f" | |
| */ | |
| if (!newFraction && (currentTime > stack->start_time ) ) newFraction = FIX_ONE; | |
| /*check for deactivation - if so generate a fraction_changed AS IF NOW WAS EXACTLY STOPTIME*/ | |
| if ((TS->stopTime > stack->start_time) && (currentTime >= TS->stopTime) ) { | |
| cycleTime = TS->stopTime - stack->start_time - stack->num_cycles * stack->cycle_interval; | |
| TS->fraction_changed = FLT2FIX( fmod(cycleTime, stack->cycle_interval) / stack->cycle_interval ); | |
| /*cf above*/ | |
| if (TS->fraction_changed < FIX_EPSILON) TS->fraction_changed = FIX_ONE; | |
| gf_node_event_out(st->udta, 6);//"fraction_changed" | |
| timesensor_deactivate(stack, TS); | |
| return; | |
| } | |
| if (! TS->loop) { | |
| if (cycleTime >= stack->cycle_interval) { | |
| TS->fraction_changed = FIX_ONE; | |
| gf_node_event_out(st->udta, 6);//"fraction_changed" | |
| timesensor_deactivate(stack, TS); | |
| return; | |
| } | |
| } | |
| if (newFraction != TS->fraction_changed) { | |
| TS->fraction_changed = newFraction; | |
| gf_node_event_out(st->udta, 6);//"fraction_changed" | |
| } | |
| } | |
| /*we're (about to be) active: VRML: | |
| "A time-dependent node is inactive until its startTime is reached. When time now becomes greater than or | |
| equal to startTime, an isActive TRUE event is generated and the time-dependent node becomes active */ | |
| if (!TS->isActive) { | |
| st->needs_unregister = 0; | |
| TS->isActive = 1; | |
| gf_node_event_out(st->udta, 7); //"isActive" | |
| TS->cycleTime = currentTime; | |
| gf_node_event_out(st->udta, 5);//"cycleTime" | |
| TS->fraction_changed = newFraction; | |
| gf_node_event_out(st->udta, 6);//"fraction_changed" | |
| } | |
| //compute cycle time | |
| if (TS->loop && (cycleTime >= stack->cycle_interval) ) { | |
| inc = 1 + (u32) ( (cycleTime - stack->cycle_interval ) / stack->cycle_interval ); | |
| stack->num_cycles += inc; | |
| cycleTime -= inc*stack->cycle_interval; | |
| TS->cycleTime = currentTime - cycleTime; | |
| gf_node_event_out(st->udta, 5);//"cycleTime" | |
| } | |
| } | |
| void compositor_init_timesensor(GF_Compositor *compositor, GF_Node *node) | |
| { | |
| TimeSensorStack *st; | |
| GF_SAFEALLOC(st, TimeSensorStack); | |
| if (!st) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate time sensor 2d stack\n")); | |
| return; | |
| } | |
| st->time_handle.UpdateTimeNode = timesensor_update_time; | |
| st->time_handle.udta = node; | |
| st->store_info = 1; | |
| st->compositor = compositor; | |
| #ifndef GPAC_DISABLE_X3D | |
| st->is_x3d = (gf_node_get_tag(node)==TAG_X3D_TimeSensor) ? 1 : 0; | |
| #endif | |
| gf_node_set_private(node, st); | |
| gf_node_set_callback_function(node, timesensor_destroy); | |
| /*time sensor needs to be run only if def'ed, otherwise it doesn't impact scene*/ | |
| //if (gf_node_get_id(node)) | |
| gf_sc_register_time_node(compositor, &st->time_handle); | |
| } | |
| void compositor_timesensor_modified(GF_Node *t) | |
| { | |
| M_TimeSensor *ts = (M_TimeSensor *)t; | |
| TimeSensorStack *stack = (TimeSensorStack *) gf_node_get_private(t); | |
| if (!stack) return; | |
| if (ts->isActive) timesensor_update_time(&stack->time_handle); | |
| if (!ts->isActive) stack->store_info = 1; | |
| if (ts->enabled) { | |
| stack->time_handle.needs_unregister = 0; | |
| if (!stack->time_handle.is_registered) { | |
| gf_sc_register_time_node(stack->compositor, &stack->time_handle); | |
| } | |
| } | |
| } | |
| #endif //!defined(GPAC_DISABLE_VRML) && !defined(GPAC_DISABLE_COMPOSITOR) |