1 /*******************************************************************************
2  * Copyright 2009-2016 Jörg Müller
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 "sequence/AnimateableProperty.h"
18 
19 #include <cstring>
20 #include <cmath>
21 #include <mutex>
22 
23 AUD_NAMESPACE_BEGIN
24 
AnimateableProperty(int count)25 AnimateableProperty::AnimateableProperty(int count) :
26 	Buffer(count * sizeof(float)), m_count(count), m_isAnimated(false)
27 {
28 	std::memset(getBuffer(), 0, count * sizeof(float));
29 }
30 
AnimateableProperty(int count,float value)31 AnimateableProperty::AnimateableProperty(int count, float value) :
32 	Buffer(count * sizeof(float)), m_count(count), m_isAnimated(false)
33 {
34 	sample_t* buf = getBuffer();
35 
36 	for(int i = 0; i < count; i++)
37 		buf[i] = value;
38 }
39 
updateUnknownCache(int start,int end)40 void AnimateableProperty::updateUnknownCache(int start, int end)
41 {
42 	float* buf = getBuffer();
43 
44 	// we could do a better interpolation than zero order, but that doesn't work with Blender's animation system
45 	// as frames are only written when changing, so to support jumps, we need zero order interpolation here.
46 	for(int i = start; i <= end; i++)
47 		std::memcpy(buf + i * m_count, buf + (start - 1) * m_count, m_count * sizeof(float));
48 }
49 
~AnimateableProperty()50 AnimateableProperty::~AnimateableProperty()
51 {
52 }
53 
getCount() const54 int AnimateableProperty::getCount() const
55 {
56 	return m_count;
57 }
58 
write(const float * data)59 void AnimateableProperty::write(const float* data)
60 {
61 	std::lock_guard<std::recursive_mutex> lock(m_mutex);
62 
63 	m_isAnimated = false;
64 	m_unknown.clear();
65 	std::memcpy(getBuffer(), data, m_count * sizeof(float));
66 }
67 
write(const float * data,int position,int count)68 void AnimateableProperty::write(const float* data, int position, int count)
69 {
70 	std::lock_guard<std::recursive_mutex> lock(m_mutex);
71 
72 	int pos = getSize() / (sizeof(float) * m_count);
73 
74 	if(!m_isAnimated)
75 		pos = 0;
76 
77 	m_isAnimated = true;
78 
79 	assureSize((count + position) * m_count * sizeof(float), true);
80 
81 	float* buf = getBuffer();
82 
83 	std::memcpy(buf + position * m_count, data, count * m_count * sizeof(float));
84 
85 	// have to fill up space between?
86 	if(pos < position)
87 	{
88 		m_unknown.push_back(Unknown(pos, position - 1));
89 
90 		// if the buffer was not animated before, we copy the previous static value
91 		if(pos == 0)
92 			pos = 1;
93 
94 		updateUnknownCache(pos, position - 1);
95 	}
96 	// otherwise it's not at the end, let's check if some unknown part got filled
97 	else
98 	{
99 		bool erased = false;
100 
101 		for(auto it = m_unknown.begin(); it != m_unknown.end(); erased ? it : it++)
102 		{
103 			erased = false;
104 
105 			// unknown area before position
106 			if(it->end < position)
107 				continue;
108 
109 			// we're after the new area, let's stop
110 			if(it->start >= position + count)
111 				break;
112 
113 			// we have an intersection, now 4 cases:
114 			// the start is included
115 			if(position <= it->start)
116 			{
117 				// the end is included
118 				if(position + count > it->end)
119 				{
120 					// simply delete
121 					it = m_unknown.erase(it);
122 					erased = true;
123 				}
124 				// the end is excluded, a second part remains
125 				else
126 				{
127 					// update second part
128 					it->start = position + count;
129 					updateUnknownCache(it->start, it->end);
130 					break;
131 				}
132 			}
133 			// start is excluded, a first part remains
134 			else
135 			{
136 				// the end is included
137 				if(position + count > it->end)
138 				{
139 					// update first part
140 					it->end = position - 1;
141 				}
142 				// the end is excluded, a second part remains
143 				else
144 				{
145 					// add another item and update both parts
146 					m_unknown.insert(it, Unknown(it->start, position - 1));
147 					it->start = position + count;
148 					updateUnknownCache(it->start, it->end);
149 				}
150 			}
151 		}
152 	}
153 }
154 
read(float position,float * out)155 void AnimateableProperty::read(float position, float* out)
156 {
157 	std::lock_guard<std::recursive_mutex> lock(m_mutex);
158 
159 	if(!m_isAnimated)
160 	{
161 		std::memcpy(out, getBuffer(), m_count * sizeof(float));
162 		return;
163 	}
164 
165 	int last = getSize() / (sizeof(float) * m_count) - 1;
166 	float t = position - std::floor(position);
167 
168 	if(position >= last)
169 	{
170 		position = last;
171 		t = 0;
172 	}
173 
174 	if(t == 0)
175 	{
176 		std::memcpy(out, getBuffer() + int(std::floor(position)) * m_count, m_count * sizeof(float));
177 	}
178 	else
179 	{
180 		int pos = int(std::floor(position)) * m_count;
181 		float t2 = t * t;
182 		float t3 = t2 * t;
183 		float m0, m1;
184 		float* p0;
185 		float* p1 = getBuffer() + pos;
186 		float* p2;
187 		float* p3;
188 		last *= m_count;
189 
190 		if(pos == 0)
191 			p0 = p1;
192 		else
193 			p0 = p1 - m_count;
194 
195 		p2 = p1 + m_count;
196 		if(pos + m_count == last)
197 			p3 = p2;
198 		else
199 			p3 = p2 + m_count;
200 
201 		for(int i = 0; i < m_count; i++)
202 		{
203 			m0 = (p2[i] - p0[i]) / 2.0f;
204 			m1 = (p3[i] - p1[i]) / 2.0f;
205 
206 			out[i] = (2 * t3 - 3 * t2 + 1) * p0[i] + (-2 * t3 + 3 * t2) * p1[i] +
207 					 (t3 - 2 * t2 + t) * m0 + (t3 - t2) * m1;
208 		}
209 	}
210 }
211 
isAnimated() const212 bool AnimateableProperty::isAnimated() const
213 {
214 	return m_isAnimated;
215 }
216 
217 AUD_NAMESPACE_END
218