]> git.cworth.org Git - vogl/blob - src/voglcommon/vogl_matrix_state.cpp
Initial vogl checkin
[vogl] / src / voglcommon / vogl_matrix_state.cpp
1 /**************************************************************************
2  *
3  * Copyright 2013-2014 RAD Game Tools and Valve Software
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25
26 // File: vogl_matrix_state.cpp
27 #include "vogl_matrix_state.h"
28
29 vogl_matrix_state::vogl_matrix_state()
30     : m_valid(false)
31 {
32     VOGL_FUNC_TRACER
33 }
34
35 vogl_matrix_state::~vogl_matrix_state()
36 {
37     VOGL_FUNC_TRACER
38 }
39
40 bool vogl_matrix_state::save_matrix_stack(const vogl_context_info &context_info, GLenum matrix, uint index, GLenum depth_get, GLenum matrix_get)
41 {
42     VOGL_FUNC_TRACER
43
44     VOGL_NOTE_UNUSED(context_info);
45
46     GL_ENTRYPOINT(glMatrixMode)(matrix);
47     if (vogl_check_gl_error())
48         return false;
49
50     GLint depth = 0;
51     GL_ENTRYPOINT(glGetIntegerv)(depth_get, &depth);
52     if (vogl_check_gl_error())
53     {
54         // This *should* work on AMD because it supports the ARB_imaging subset on compat contexts, and it supports the GL_COLOR matrix and the max stack depth is reported as 10.
55         if (depth_get == GL_COLOR_MATRIX_STACK_DEPTH)
56         {
57             vogl_warning_printf("%s: Using GL_COLOR_MATRIX_STACK_DEPTH workaround for AMD drivers - this will purposely force a stack underflow!\n", VOGL_METHOD_NAME);
58
59             vogl::vector<matrix44D> matrices;
60             for (;;)
61             {
62                 matrix44D matrix;
63                 GL_ENTRYPOINT(glGetDoublev)(matrix_get, matrix.get_ptr());
64                 if (vogl_check_gl_error())
65                     return false;
66
67                 matrices.push_back(matrix);
68
69                 GL_ENTRYPOINT(glPopMatrix)();
70                 bool failed_popping = vogl_check_gl_error();
71
72                 GL_ENTRYPOINT(glGetDoublev)(matrix_get, matrix.get_ptr());
73                 // AMD fails with a stack underflow on the get, not the pop - at least during tracing?
74                 if (vogl_check_gl_error())
75                     failed_popping = true;
76
77                 if (failed_popping)
78                     break;
79             }
80
81             for (int i = matrices.size() - 1; i >= 0; i--)
82             {
83                 if (i != static_cast<int>(matrices.size()) - 1)
84                 {
85                     GL_ENTRYPOINT(glPushMatrix)();
86                     if (vogl_check_gl_error())
87                         return false;
88                 }
89
90                 GL_ENTRYPOINT(glLoadMatrixd)(matrices[i].get_ptr());
91                 if (vogl_check_gl_error())
92                     return false;
93             }
94
95             depth = matrices.size();
96         }
97         else
98         {
99             return false;
100         }
101     }
102
103     if (depth < 1)
104         return false;
105
106     matrix_vec &vec = m_matrices[matrix_key(matrix, index)];
107     vec.resize(depth);
108
109     // deepest .. current
110     // 0       1   2
111
112     for (int i = 0; i < depth; i++)
113     {
114         matrix44D mat;
115         GL_ENTRYPOINT(glGetDoublev)(matrix_get, mat.get_ptr());
116         if (vogl_check_gl_error())
117             return false;
118
119         vec[depth - 1 - i] = mat;
120
121         if (i != (depth - 1))
122         {
123             GL_ENTRYPOINT(glPopMatrix)();
124             if (vogl_check_gl_error())
125                 return false;
126         }
127     }
128
129     for (int i = 1; i < depth; i++)
130     {
131         GL_ENTRYPOINT(glPushMatrix)();
132         if (vogl_check_gl_error())
133             return false;
134
135         GL_ENTRYPOINT(glLoadMatrixd)(vec[i].get_ptr());
136         if (vogl_check_gl_error())
137             return false;
138     }
139
140     return true;
141 }
142
143 bool vogl_matrix_state::restore_matrix_stack(const vogl_context_info &context_info, GLenum matrix, uint index) const
144 {
145     VOGL_FUNC_TRACER
146
147     VOGL_NOTE_UNUSED(context_info);
148
149     const matrix_vec *pVec = m_matrices.find_value(matrix_key(matrix, index));
150     if ((!pVec) || (pVec->size() < 1))
151         return false;
152
153     GL_ENTRYPOINT(glMatrixMode)(matrix);
154     if (vogl_check_gl_error())
155         return false;
156
157     // deepest .. current
158     // 0       1   2
159
160     for (uint i = 0; i < pVec->size(); i++)
161     {
162         if (i)
163         {
164             GL_ENTRYPOINT(glPushMatrix)();
165             if (vogl_check_gl_error())
166                 return false;
167         }
168
169         GL_ENTRYPOINT(glLoadMatrixd)((*pVec)[i].get_ptr());
170         if (vogl_check_gl_error())
171             return false;
172     }
173
174     return true;
175 }
176
177 bool vogl_matrix_state::snapshot(const vogl_context_info &context_info)
178 {
179     VOGL_FUNC_TRACER
180
181     clear();
182
183     bool any_errors = false;
184
185     VOGL_CHECK_GL_ERROR;
186
187     vogl_scoped_state_saver state_saver(cGSTActiveTexture, cGSTMatrixMode);
188
189     if (vogl_check_gl_error())
190         any_errors = true;
191
192     if (!save_matrix_stack(context_info, GL_PROJECTION, 0, GL_PROJECTION_STACK_DEPTH, GL_PROJECTION_MATRIX))
193         any_errors = true;
194
195     if (!save_matrix_stack(context_info, GL_MODELVIEW, 0, GL_MODELVIEW_STACK_DEPTH, GL_MODELVIEW_MATRIX))
196         any_errors = true;
197
198     if (!save_matrix_stack(context_info, GL_COLOR, 0, GL_COLOR_MATRIX_STACK_DEPTH, GL_COLOR_MATRIX))
199         any_errors = true;
200
201     for (uint texcoord_index = 0; texcoord_index < context_info.get_max_texture_coords(); texcoord_index++)
202     {
203         GL_ENTRYPOINT(glActiveTexture)(GL_TEXTURE0 + texcoord_index);
204
205         if (vogl_check_gl_error())
206             any_errors = true;
207
208         if (!save_matrix_stack(context_info, GL_TEXTURE, texcoord_index, GL_TEXTURE_STACK_DEPTH, GL_TEXTURE_MATRIX))
209             any_errors = true;
210     }
211
212     for (uint i = 0; i < context_info.get_max_arb_program_matrices(); i++)
213     {
214         if (!save_matrix_stack(context_info, GL_MATRIX0_ARB + i, 0, GL_CURRENT_MATRIX_STACK_DEPTH_ARB, GL_CURRENT_MATRIX_ARB))
215             any_errors = true;
216     }
217
218     if (any_errors)
219         clear();
220     else
221         m_valid = true;
222
223     return !any_errors;
224 }
225
226 bool vogl_matrix_state::restore(const vogl_context_info &context_info) const
227 {
228     VOGL_FUNC_TRACER
229
230     if (!m_valid)
231         return false;
232
233     bool any_errors = false;
234
235     VOGL_CHECK_GL_ERROR;
236
237     vogl_scoped_state_saver state_saver(cGSTActiveTexture, cGSTMatrixMode);
238
239     if (vogl_check_gl_error())
240         any_errors = true;
241
242     if (!restore_matrix_stack(context_info, GL_PROJECTION, 0))
243         any_errors = true;
244
245     if (!restore_matrix_stack(context_info, GL_MODELVIEW, 0))
246         any_errors = true;
247
248     if (!restore_matrix_stack(context_info, GL_COLOR, 0))
249         any_errors = true;
250
251     // TODO: Check to make sure we can actually restore the proper # of matrices
252     for (uint texcoord_index = 0; texcoord_index < context_info.get_max_texture_coords(); texcoord_index++)
253     {
254         GL_ENTRYPOINT(glActiveTexture)(GL_TEXTURE0 + texcoord_index);
255
256         if (vogl_check_gl_error())
257             any_errors = true;
258
259         restore_matrix_stack(context_info, GL_TEXTURE, texcoord_index);
260     }
261
262     // TODO: Check to make sure we can actually restore the proper # of matrices
263     for (uint i = 0; i < context_info.get_max_arb_program_matrices(); i++)
264     {
265         restore_matrix_stack(context_info, GL_MATRIX0_ARB + i, 0);
266     }
267
268     return !any_errors;
269 }
270
271 void vogl_matrix_state::clear()
272 {
273     VOGL_FUNC_TRACER
274
275     m_valid = false;
276     m_matrices.clear();
277 }
278
279 bool vogl_matrix_state::serialize(json_node &node, vogl_blob_manager &blob_manager) const
280 {
281     VOGL_FUNC_TRACER
282
283     VOGL_NOTE_UNUSED(blob_manager);
284
285     if (!m_valid)
286         return false;
287
288     node.init_array();
289
290     for (matrix_map::const_iterator it = m_matrices.begin(); it != m_matrices.end(); ++it)
291     {
292         json_node &obj_node = node.add_object();
293         obj_node.add_key_value("matrix", g_gl_enums.find_gl_name(it->first.m_target));
294         if (it->first.m_index)
295             obj_node.add_key_value("index", it->first.m_index);
296
297         json_node &matrices_node = obj_node.add_array("matrices");
298         for (uint i = 0; i < it->second.size(); i++)
299         {
300             json_node &matrix_node = matrices_node.add_array();
301             for (uint j = 0; j < 4 * 4; j++)
302                 matrix_node.add_value(it->second[i].get_ptr()[j]);
303         }
304     }
305
306     return true;
307 }
308
309 bool vogl_matrix_state::deserialize(const json_node &node, const vogl_blob_manager &blob_manager)
310 {
311     VOGL_FUNC_TRACER
312
313     VOGL_NOTE_UNUSED(blob_manager);
314
315     clear();
316
317     if (!node.is_array() || !node.are_all_children_objects())
318     {
319         VOGL_ASSERT_ALWAYS;
320         return false;
321     }
322
323     for (uint i = 0; i < node.size(); i++)
324     {
325         const json_node &obj_node = *node.get_child(i);
326
327         GLenum matrix = vogl_get_json_value_as_enum(obj_node, "matrix", GL_NONE);
328         if (matrix == GL_NONE)
329         {
330             clear();
331             return false;
332         }
333
334         uint index = obj_node.value_as_uint32("index");
335         if ((index) && (matrix != GL_TEXTURE))
336         {
337             clear();
338             return false;
339         }
340
341         const json_node *pMatrices = obj_node.find_child_array("matrices");
342         if ((!pMatrices) || (!pMatrices->are_all_children_arrays()))
343         {
344             clear();
345             return false;
346         }
347
348         matrix_vec &matrices = m_matrices[matrix_key(matrix, index)];
349         matrices.resize(pMatrices->size());
350
351         for (uint i = 0; i < matrices.size(); i++)
352         {
353             const json_node *pMatrix = pMatrices->get_value_as_array(i);
354             if ((!pMatrix) || (!pMatrix->is_array()) || (pMatrix->size() != 4 * 4))
355                 return false;
356
357             for (uint j = 0; j < 4 * 4; j++)
358                 matrices[i].get_ptr()[j] = pMatrix->value_as_double(j);
359         }
360     }
361
362     m_valid = true;
363
364     return true;
365 }
366
367 uint vogl_matrix_state::get_matrix_stack_depth(GLenum target, uint index) const
368 {
369     matrix_map::const_iterator iter = m_matrices.find(matrix_key(target, index));
370     if (iter == m_matrices.end())
371     {
372         return 0;
373     }
374
375     return iter->second.size();
376 }
377
378 const matrix44D *vogl_matrix_state::get_matrix(GLenum target, uint index, uint depth) const
379 {
380     matrix_map::const_iterator iter = m_matrices.find(matrix_key(target, index));
381     if (iter == m_matrices.end())
382     {
383         return NULL;
384     }
385
386     if (depth >= iter->second.size())
387     {
388         return NULL;
389     }
390
391     return &(iter->second[depth]);
392 }