Skip to content

Commit 3cb37c6

Browse files
committed
add animation time range helper
1 parent 3b8e9d4 commit 3cb37c6

5 files changed

Lines changed: 207 additions & 0 deletions

File tree

include/ak/animation.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,20 @@ AK_EXPORT
244244
size_t
245245
ak_animationsCount(struct AkDoc * __restrict doc);
246246

247+
/*!
248+
* @brief Compute the authored time range covered by an animation and its
249+
* child animation tree.
250+
*
251+
* The function scans INPUT sampler accessors only; it does not allocate
252+
* and it does not resolve channel targets. Returns false when no valid
253+
* key time accessor is reachable.
254+
*/
255+
AK_EXPORT
256+
bool
257+
ak_animationTimeRange(AkAnimation * __restrict anim,
258+
float * __restrict outStart,
259+
float * __restrict outEnd);
260+
247261
/*!
248262
* @brief Convenience over `ak_animationsCompatibleSet` that walks the
249263
* document's animation libraries itself — so callers don't have to

src/anim/conflict.c

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,37 @@
4848

4949
#include "../common.h"
5050

51+
static bool
52+
ak_animationSamplerTimeRange(AkAnimSampler * __restrict sampler,
53+
float * __restrict outStart,
54+
float * __restrict outEnd) {
55+
AkInput *inp;
56+
AkAccessor *acc;
57+
AkBuffer *buff;
58+
const float *times;
59+
uint32_t count;
60+
61+
if (!sampler) return false;
62+
63+
for (inp = sampler->input; inp; inp = inp->next) {
64+
if (inp->semantic != AK_INPUT_INPUT)
65+
continue;
66+
67+
acc = inp->accessor;
68+
buff = acc ? acc->buffer : NULL;
69+
if (!buff || !buff->data || acc->count == 0)
70+
continue;
71+
72+
times = (const float *)((const char *)buff->data + acc->byteOffset);
73+
count = acc->count;
74+
*outStart = times[0];
75+
*outEnd = times[count - 1];
76+
return true;
77+
}
78+
79+
return false;
80+
}
81+
5182
AK_EXPORT
5283
bool
5384
ak_animationsConflict(AkContext * __restrict ctx,
@@ -129,6 +160,62 @@ ak_animationsCount(AkDoc * __restrict doc) {
129160
return count;
130161
}
131162

163+
AK_EXPORT
164+
bool
165+
ak_animationTimeRange(AkAnimation * __restrict anim,
166+
float * __restrict outStart,
167+
float * __restrict outEnd) {
168+
AkAnimation *stack[256], *next;
169+
AkChannel *ch;
170+
AkAnimSampler *sampler;
171+
float start, end, minTime, maxTime;
172+
int top;
173+
bool found, includeSiblings;
174+
175+
if (!anim || !outStart || !outEnd)
176+
return false;
177+
178+
top = 0;
179+
found = false;
180+
includeSiblings = false;
181+
minTime = 0.0f;
182+
maxTime = 0.0f;
183+
184+
while (anim) {
185+
for (ch = anim->channel; ch; ch = ch->next) {
186+
sampler = ak_getObjectByUrl(&ch->source);
187+
if (!ak_animationSamplerTimeRange(sampler, &start, &end))
188+
continue;
189+
190+
if (!found || start < minTime) minTime = start;
191+
if (!found || end > maxTime) maxTime = end;
192+
found = true;
193+
}
194+
195+
if (anim->animation) {
196+
next = includeSiblings ? anim->next : NULL;
197+
if (next && top < (int)(sizeof(stack) / sizeof(stack[0])))
198+
stack[top++] = next;
199+
anim = anim->animation;
200+
includeSiblings = true;
201+
} else if (includeSiblings && anim->next) {
202+
anim = anim->next;
203+
} else if (top > 0) {
204+
anim = stack[--top];
205+
includeSiblings = true;
206+
} else {
207+
anim = NULL;
208+
}
209+
}
210+
211+
if (!found)
212+
return false;
213+
214+
*outStart = minTime;
215+
*outEnd = maxTime;
216+
return true;
217+
}
218+
132219
AK_EXPORT
133220
size_t
134221
ak_animationsCompatibleSetFromDoc(AkContext * __restrict ctx,

test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
add_executable(assetkit_tests
22
runner.c
3+
src/core/animation.c
34
src/core/memory.c
45
src/core/coord.c
56
src/core/format_edge.c

test/src/core/animation.c

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
* Copyright (C) 2020 Recep Aslantas
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "../test_export_common.h"
18+
19+
static AkAnimSampler *
20+
ak_test_make_time_sampler(AkHeap *heap,
21+
void *parent,
22+
const float *times,
23+
uint32_t count) {
24+
AkAnimSampler *sampler;
25+
AkInput *timeInput;
26+
AkInput *valueInput;
27+
static const float values[2] = {0.0f, 1.0f};
28+
29+
sampler = ak_heap_calloc(heap, parent, sizeof(*sampler));
30+
timeInput = ak_heap_calloc(heap, sampler, sizeof(*timeInput));
31+
valueInput = ak_heap_calloc(heap, sampler, sizeof(*valueInput));
32+
if (!sampler || !timeInput || !valueInput)
33+
return NULL;
34+
35+
timeInput->semantic = AK_INPUT_INPUT;
36+
timeInput->accessor = ak_test_make_float_accessor(heap,
37+
timeInput,
38+
times,
39+
1,
40+
count);
41+
valueInput->semantic = AK_INPUT_OUTPUT;
42+
valueInput->accessor = ak_test_make_float_accessor(heap,
43+
valueInput,
44+
values,
45+
1,
46+
2);
47+
if (!timeInput->accessor || !valueInput->accessor)
48+
return NULL;
49+
50+
timeInput->next = valueInput;
51+
sampler->input = timeInput;
52+
return sampler;
53+
}
54+
55+
static AkAnimation *
56+
ak_test_make_time_animation(AkHeap *heap,
57+
void *parent,
58+
const float *times,
59+
uint32_t count) {
60+
AkAnimation *anim;
61+
AkChannel *channel;
62+
63+
anim = ak_heap_calloc(heap, parent, sizeof(*anim));
64+
channel = ak_heap_calloc(heap, anim, sizeof(*channel));
65+
if (!anim || !channel)
66+
return NULL;
67+
68+
anim->sampler = ak_test_make_time_sampler(heap, anim, times, count);
69+
channel->source.ptr = anim->sampler;
70+
anim->channel = channel;
71+
return anim;
72+
}
73+
74+
TEST_IMPL(animation_time_range_walks_child_tree) {
75+
AkHeap *heap;
76+
AkAnimation *root;
77+
AkAnimation *childA;
78+
AkAnimation *childB;
79+
float start, end;
80+
const float rootTimes[2] = {1.0f, 3.0f};
81+
const float childATimes[2] = {0.25f, 2.0f};
82+
const float childBTimes[2] = {4.0f, 7.5f};
83+
84+
heap = ak_heap_new(NULL, NULL, NULL);
85+
ASSERT(heap != NULL);
86+
87+
root = ak_test_make_time_animation(heap, NULL, rootTimes, 2);
88+
childA = ak_test_make_time_animation(heap, root, childATimes, 2);
89+
childB = ak_test_make_time_animation(heap, root, childBTimes, 2);
90+
ASSERT(root != NULL);
91+
ASSERT(childA != NULL);
92+
ASSERT(childB != NULL);
93+
94+
root->animation = childA;
95+
childA->next = childB;
96+
97+
ASSERT(ak_animationTimeRange(root, &start, &end));
98+
ASSERT(start == 0.25f);
99+
ASSERT(end == 7.5f);
100+
101+
ak_heap_destroy(heap);
102+
TEST_SUCCESS
103+
}

test/tests.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
TEST_DECLARE(heap)
2929
TEST_DECLARE(heap_multiple)
30+
TEST_DECLARE(animation_time_range_walks_child_tree)
3031
TEST_DECLARE(coord_change_doc_converts_scene_payloads)
3132
TEST_DECLARE(mesh_triangulate_polygon_sets_triangle_mode)
3233
TEST_DECLARE(instance_attach_helpers)
@@ -235,6 +236,7 @@ TEST_DECLARE(format_edge_cases)
235236
TEST_LIST {
236237
TEST_ENTRY(heap)
237238
TEST_ENTRY(heap_multiple)
239+
TEST_ENTRY(animation_time_range_walks_child_tree)
238240
TEST_ENTRY(coord_change_doc_converts_scene_payloads)
239241
TEST_ENTRY(mesh_triangulate_polygon_sets_triangle_mode)
240242
TEST_ENTRY(instance_attach_helpers)

0 commit comments

Comments
 (0)