PageRenderTime 208ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/particle/renderermodule.c

http://caseman.googlecode.com/
C | 428 lines | 336 code | 56 blank | 36 comment | 25 complexity | 9eddf47c9f0725b98875d5b5f8b925b4 MD5 | raw file
  1. /* Native-code renderers
  2. *
  3. * $Id $ */
  4. #include <Python.h>
  5. #include <structmember.h>
  6. #ifdef __APPLE__
  7. #include <OpenGL/gl.h>
  8. #include <OpenGL/glu.h>
  9. #else
  10. #include <GL/gl.h>
  11. #include <GL/glu.h>
  12. #endif
  13. #include "vector.h"
  14. #include "group.h"
  15. typedef struct {
  16. PyObject_HEAD
  17. GroupObject *pgroup;
  18. } RendererObject;
  19. /* Base renderer methods */
  20. static PyObject *
  21. Renderer_set_group(RendererObject *self, GroupObject *new_group)
  22. {
  23. PyObject *r;
  24. if (self->pgroup != NULL && (PyObject *)self->pgroup != Py_None) {
  25. /* call self.group.set_renderer(None) */
  26. r = PyObject_CallMethod((PyObject *)self->pgroup, "set_renderer", "O", Py_None);
  27. Py_XDECREF(r);
  28. if (r == NULL || PyErr_Occurred())
  29. return NULL;
  30. }
  31. Py_INCREF(new_group);
  32. Py_XDECREF(self->pgroup);
  33. self->pgroup = new_group;
  34. Py_INCREF(Py_None);
  35. return Py_None;
  36. }
  37. static void
  38. Renderer_dealloc(RendererObject *self) {
  39. Py_CLEAR(self->pgroup);
  40. PyObject_Del(self);
  41. }
  42. /* --------------------------------------------------------------------- */
  43. static PyTypeObject PointRenderer_Type;
  44. typedef struct {
  45. PyObject_HEAD
  46. GroupObject *pgroup;
  47. float point_size;
  48. } PointRendererObject;
  49. static int
  50. PointRenderer_init(PointRendererObject *self, PyObject *args, PyObject *kwargs)
  51. {
  52. static char *kwlist[] = {"point_size", NULL};
  53. if (!PyArg_ParseTupleAndKeywords(args, kwargs, "f:__init__",kwlist,
  54. &self->point_size))
  55. return -1;
  56. self->pgroup = NULL;
  57. return 0;
  58. }
  59. static PyObject *
  60. PointRenderer_draw(PointRendererObject *self)
  61. {
  62. Particle *p;
  63. int GL_error;
  64. if (self->pgroup == NULL) {
  65. PyErr_SetString(PyExc_RuntimeError, "cannot draw, no group set");
  66. return NULL;
  67. }
  68. p = self->pgroup->plist->p;
  69. glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
  70. glEnableClientState(GL_VERTEX_ARRAY);
  71. glEnableClientState(GL_COLOR_ARRAY);
  72. glPointSize(self->point_size);
  73. glVertexPointer(3, GL_FLOAT, sizeof(Particle), &p[0].position);
  74. glColorPointer(4, GL_FLOAT, sizeof(Particle), &p[0].color);
  75. glDrawArrays(GL_POINTS, 0, GroupObject_ActiveCount(self->pgroup));
  76. glPopClientAttrib();
  77. GL_error = glGetError();
  78. if (GL_error != GL_NO_ERROR) {
  79. PyErr_Format(PyExc_RuntimeError, "GL error %d", GL_error);
  80. return NULL;
  81. }
  82. Py_INCREF(Py_None);
  83. return Py_None;
  84. }
  85. static struct PyMemberDef PointRenderer_members[] = {
  86. {"point_size", T_FLOAT, offsetof(PointRendererObject, point_size), RESTRICTED,
  87. "Size of GL_POINTS drawn"},
  88. {"group", T_OBJECT, offsetof(PointRendererObject, pgroup), RO,
  89. "Particle group this renderer is bound to"},
  90. {NULL}
  91. };
  92. static PyMethodDef PointRenderer_methods[] = {
  93. {"set_group", (PyCFunction)Renderer_set_group, METH_O,
  94. PyDoc_STR("set_group(group) -> None\n"
  95. "Sets the particle group for this renderer")},
  96. {"draw", (PyCFunction)PointRenderer_draw, METH_NOARGS,
  97. PyDoc_STR("Draw the particles using points")},
  98. {NULL, NULL} /* sentinel */
  99. };
  100. PyDoc_STRVAR(PointRenderer__doc__,
  101. "Simple particle renderer using GL_POINTS. All particles in the\n"
  102. "group are rendered with the same point size\n\n"
  103. "PointRenderer(point_size)\n\n"
  104. "point_size -- Size of GL points (float)");
  105. static PyTypeObject PointRenderer_Type = {
  106. /* The ob_type field must be initialized in the module init function
  107. * to be portable to Windows without using C++. */
  108. PyObject_HEAD_INIT(NULL)
  109. 0, /*ob_size*/
  110. "renderer.PointRenderer", /*tp_name*/
  111. sizeof(PointRendererObject), /*tp_basicsize*/
  112. 0, /*tp_itemsize*/
  113. /* methods */
  114. (destructor)Renderer_dealloc, /*tp_dealloc*/
  115. 0, /*tp_print*/
  116. 0, /*tp_getattr*/
  117. 0, /*tp_setattr*/
  118. 0, /*tp_compare*/
  119. 0, /*tp_repr*/
  120. 0, /*tp_as_number*/
  121. 0, /*tp_as_sequence*/
  122. 0, /*tp_as_mapping*/
  123. 0, /*tp_hash*/
  124. 0, /*tp_call*/
  125. 0, /*tp_str*/
  126. PyObject_GenericGetAttr, /*tp_getattro*/
  127. 0, /*tp_setattro*/
  128. 0, /*tp_as_buffer*/
  129. Py_TPFLAGS_DEFAULT, /*tp_flags*/
  130. PointRenderer__doc__, /*tp_doc*/
  131. 0, /*tp_traverse*/
  132. 0, /*tp_clear*/
  133. 0, /*tp_richcompare*/
  134. 0, /*tp_weaklistoffset*/
  135. 0, /*tp_iter*/
  136. 0, /*tp_iternext*/
  137. PointRenderer_methods, /*tp_methods*/
  138. PointRenderer_members, /*tp_members*/
  139. 0, /*tp_getset*/
  140. 0, /*tp_base*/
  141. 0, /*tp_dict*/
  142. 0, /*tp_descr_get*/
  143. 0, /*tp_descr_set*/
  144. 0, /*tp_dictoffset*/
  145. (initproc)PointRenderer_init, /*tp_init*/
  146. 0, /*tp_alloc*/
  147. 0, /*tp_new*/
  148. 0, /*tp_free*/
  149. 0, /*tp_is_gc*/
  150. };
  151. /* --------------------------------------------------------------------- */
  152. static PyTypeObject BillboardRenderer_Type;
  153. static int
  154. BillboardRenderer_init(RendererObject *self, PyObject *args)
  155. {
  156. self->pgroup = NULL;
  157. return 0;
  158. }
  159. typedef struct {
  160. float tex[2];
  161. union {
  162. unsigned char color[4];
  163. unsigned long colorl;
  164. };
  165. struct {
  166. float x; float y; float z;
  167. } vert;
  168. } BBData;
  169. #define BILLBOARD_BATCH_SIZE 5000
  170. static unsigned short billboard_indices[BILLBOARD_BATCH_SIZE*6];
  171. static void
  172. init_billboard_indices(void) {
  173. int i;
  174. unsigned short v = 0;
  175. for (i = 0; i < BILLBOARD_BATCH_SIZE*6; i += 6) {
  176. billboard_indices[i] = v;
  177. billboard_indices[i+1] = v+1;
  178. billboard_indices[i+2] = v+3;
  179. billboard_indices[i+3] = v+1;
  180. billboard_indices[i+4] = v+2;
  181. billboard_indices[i+5] = v+3;
  182. v += 4;
  183. }
  184. }
  185. static PyObject *
  186. BillboardRenderer_draw(RendererObject *self)
  187. {
  188. Particle *p;
  189. int GL_error;
  190. unsigned long remaining, batch_size;
  191. BBData data[BILLBOARD_BATCH_SIZE*4];
  192. register int i;
  193. float mvmatrix[16];
  194. Vec3 vright, vright_unit, vup, vup_unit;
  195. if (self->pgroup == NULL) {
  196. PyErr_SetString(PyExc_RuntimeError, "cannot draw, no group set");
  197. return NULL;
  198. }
  199. p = self->pgroup->plist->p;
  200. remaining = GroupObject_ActiveCount(self->pgroup);
  201. /* Get the alignment vectors from the view matrix */
  202. glGetFloatv(GL_MODELVIEW_MATRIX, mvmatrix);
  203. vright_unit.x = mvmatrix[0];
  204. vright_unit.y = mvmatrix[4];
  205. vright_unit.z = mvmatrix[8];
  206. Vec3_normalize(&vright_unit, &vright_unit);
  207. vup_unit.x = mvmatrix[1];
  208. vup_unit.y = mvmatrix[5];
  209. vup_unit.z = mvmatrix[9];
  210. Vec3_normalize(&vup_unit, &vup_unit);
  211. glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
  212. glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  213. glEnableClientState(GL_VERTEX_ARRAY);
  214. glEnableClientState(GL_COLOR_ARRAY);
  215. while (remaining) {
  216. if (remaining > BILLBOARD_BATCH_SIZE) {
  217. batch_size = BILLBOARD_BATCH_SIZE;
  218. remaining -= BILLBOARD_BATCH_SIZE;
  219. } else {
  220. batch_size = remaining;
  221. remaining = 0;
  222. }
  223. for (i = 0; i < batch_size*4; i += 4) {
  224. #define POINT0 i
  225. #define POINT1 i+1
  226. #define POINT2 i+2
  227. #define POINT3 i+3
  228. /*
  229. POINT3 POINT2
  230. +-------------+
  231. |\ |
  232. | \ |
  233. | \ |
  234. | + ---- | --- Particle position
  235. | \ |
  236. | \ |
  237. | \|
  238. +-------------+
  239. POINT0 POINT1
  240. */
  241. /* vertex coords */
  242. Vec3_scalar_mul(&vright, &vright_unit, p->size.x * 0.5);
  243. Vec3_scalar_mul(&vup, &vup_unit, p->size.y * 0.5);
  244. /* TODO support rotation */
  245. Vec3_sub(&data[POINT0].vert, &p->position, &vright);
  246. Vec3_subi(&data[POINT0].vert, &vup);
  247. Vec3_add(&data[POINT1].vert, &p->position, &vright);
  248. Vec3_subi(&data[POINT1].vert, &vup);
  249. Vec3_add(&data[POINT2].vert, &p->position, &vright);
  250. Vec3_addi(&data[POINT2].vert, &vup);
  251. Vec3_sub(&data[POINT3].vert, &p->position, &vright);
  252. Vec3_addi(&data[POINT3].vert, &vup);
  253. /* colors */
  254. data[POINT0].color[0] = p->color.r * 255;
  255. data[POINT0].color[1] = p->color.b * 255;
  256. data[POINT0].color[2] = p->color.g * 255;
  257. data[POINT0].color[3] = p->color.a * 255;
  258. data[POINT1].colorl = data[POINT0].colorl;
  259. data[POINT2].colorl = data[POINT0].colorl;
  260. data[POINT3].colorl = data[POINT0].colorl;
  261. /* texture coords */
  262. data[POINT0].tex[0] = 0.0f;
  263. data[POINT0].tex[1] = 0.0f;
  264. data[POINT1].tex[0] = 1.0f;
  265. data[POINT1].tex[1] = 0.0f;
  266. data[POINT2].tex[0] = 1.0f;
  267. data[POINT2].tex[1] = 1.0f;
  268. data[POINT3].tex[0] = 0.0f;
  269. data[POINT3].tex[1] = 1.0f;
  270. p++;
  271. }
  272. glInterleavedArrays(GL_T2F_C4UB_V3F, 0, data);
  273. glDrawElements(GL_TRIANGLES, batch_size*6, GL_UNSIGNED_SHORT, billboard_indices);
  274. }
  275. glPopClientAttrib();
  276. GL_error = glGetError();
  277. if (GL_error != GL_NO_ERROR) {
  278. PyErr_Format(PyExc_RuntimeError, "GL error %d", GL_error);
  279. return NULL;
  280. }
  281. Py_INCREF(Py_None);
  282. return Py_None;
  283. }
  284. static struct PyMemberDef BillboardRenderer_members[] = {
  285. {"group", T_OBJECT, offsetof(RendererObject, pgroup), RO,
  286. "Particle group this renderer is bound to"},
  287. {NULL}
  288. };
  289. static PyMethodDef BillboardRenderer_methods[] = {
  290. {"set_group", (PyCFunction)Renderer_set_group, METH_O,
  291. PyDoc_STR("set_group(group) -> None\n"
  292. "Sets the particle group for this renderer")},
  293. {"draw", (PyCFunction)BillboardRenderer_draw, METH_NOARGS,
  294. PyDoc_STR("Draw the particles using textured billboard quads")},
  295. {NULL, NULL} /* sentinel */
  296. };
  297. PyDoc_STRVAR(BillboardRenderer__doc__,
  298. "Particle renderer using textured billboard-aligned quads\n"
  299. "quads are aligned orthogonal to the model-view matrix\n\n"
  300. "BillboardRenderer()\n");
  301. static PyTypeObject BillboardRenderer_Type = {
  302. /* The ob_type field must be initialized in the module init function
  303. * to be portable to Windows without using C++. */
  304. PyObject_HEAD_INIT(NULL)
  305. 0, /*ob_size*/
  306. "renderer.BillboardRenderer", /*tp_name*/
  307. sizeof(RendererObject), /*tp_basicsize*/
  308. 0, /*tp_itemsize*/
  309. /* methods */
  310. (destructor)Renderer_dealloc, /*tp_dealloc*/
  311. 0, /*tp_print*/
  312. 0, /*tp_getattr*/
  313. 0, /*tp_setattr*/
  314. 0, /*tp_compare*/
  315. 0, /*tp_repr*/
  316. 0, /*tp_as_number*/
  317. 0, /*tp_as_sequence*/
  318. 0, /*tp_as_mapping*/
  319. 0, /*tp_hash*/
  320. 0, /*tp_call*/
  321. 0, /*tp_str*/
  322. PyObject_GenericGetAttr, /*tp_getattro*/
  323. 0, /*tp_setattro*/
  324. 0, /*tp_as_buffer*/
  325. Py_TPFLAGS_DEFAULT, /*tp_flags*/
  326. BillboardRenderer__doc__, /*tp_doc*/
  327. 0, /*tp_traverse*/
  328. 0, /*tp_clear*/
  329. 0, /*tp_richcompare*/
  330. 0, /*tp_weaklistoffset*/
  331. 0, /*tp_iter*/
  332. 0, /*tp_iternext*/
  333. BillboardRenderer_methods, /*tp_methods*/
  334. BillboardRenderer_members, /*tp_members*/
  335. 0, /*tp_getset*/
  336. 0, /*tp_base*/
  337. 0, /*tp_dict*/
  338. 0, /*tp_descr_get*/
  339. 0, /*tp_descr_set*/
  340. 0, /*tp_dictoffset*/
  341. (initproc)BillboardRenderer_init, /*tp_init*/
  342. 0, /*tp_alloc*/
  343. 0, /*tp_new*/
  344. 0, /*tp_free*/
  345. 0, /*tp_is_gc*/
  346. };
  347. PyMODINIT_FUNC
  348. initrenderer(void)
  349. {
  350. PyObject *m;
  351. /* Bind tp_new and tp_alloc here to appease certain compilers */
  352. PointRenderer_Type.tp_alloc = PyType_GenericAlloc;
  353. PointRenderer_Type.tp_new = PyType_GenericNew;
  354. if (PyType_Ready(&PointRenderer_Type) < 0)
  355. return;
  356. /* Bind tp_new and tp_alloc here to appease certain compilers */
  357. BillboardRenderer_Type.tp_alloc = PyType_GenericAlloc;
  358. BillboardRenderer_Type.tp_new = PyType_GenericNew;
  359. if (PyType_Ready(&BillboardRenderer_Type) < 0)
  360. return;
  361. /* Create the module and add the types */
  362. m = Py_InitModule3("renderer", NULL, "Particle Renderers");
  363. if (m == NULL)
  364. return;
  365. Py_INCREF(&PointRenderer_Type);
  366. PyModule_AddObject(m, "PointRenderer", (PyObject *)&PointRenderer_Type);
  367. Py_INCREF(&BillboardRenderer_Type);
  368. PyModule_AddObject(m, "BillboardRenderer", (PyObject *)&BillboardRenderer_Type);
  369. init_billboard_indices();
  370. }