From 440b9247ed40c9bce22e84bc8d2a29853ff764f3 Mon Sep 17 00:00:00 2001 From: alsotoes Date: Sun, 3 Apr 2022 23:21:48 -0500 Subject: [PATCH] first backup --- FullHenoc/.DS_Store | Bin 0 -> 15364 bytes FullHenoc/CfrmHenoc.cpp | 495 +++ FullHenoc/CfrmHenoc.h | 46 + FullHenoc/CfrmMundo.cpp | 44 + FullHenoc/CfrmMundo.h | 23 + FullHenoc/CfrmPeCaLi.cpp | 86 + FullHenoc/CfrmPeCaLi.h | 26 + FullHenoc/HBall.cpp | 28 + FullHenoc/HBall.h | 31 + FullHenoc/HBox.cpp | 28 + FullHenoc/HBox.h | 31 + FullHenoc/HLine.cpp | 23 + FullHenoc/HLine.h | 31 + FullHenoc/HObject.cpp | 44 + FullHenoc/HObject.h | 50 + FullHenoc/Henoc.pro | 35 + FullHenoc/diagramscene.cpp | 220 ++ FullHenoc/diagramscene.h | 58 + FullHenoc/diagramscene.pro | 20 + FullHenoc/diagramscene.qrc | 47 + FullHenoc/frmCatapulta.ui | 201 ++ FullHenoc/frmHenoc.ui | 292 ++ FullHenoc/frmMundo.ui | 212 ++ FullHenoc/frmPeCaLi.ui | 199 ++ FullHenoc/glwidget.cpp | 219 ++ FullHenoc/glwidget.h | 37 + FullHenoc/henocUniverseI.cpp | 889 ++++++ FullHenoc/henocUniverseI.h | 305 ++ FullHenoc/images/Botones/Config.png | Bin 0 -> 61916 bytes FullHenoc/images/Botones/Enoch21.JPG | Bin 0 -> 16857 bytes FullHenoc/images/Botones/Folder-Secure.png | Bin 0 -> 63028 bytes FullHenoc/images/Botones/My-Documents.png | Bin 0 -> 65055 bytes FullHenoc/images/Botones/SSave.png | Bin 0 -> 24370 bytes FullHenoc/images/Botones/Symbol-Stop.png | Bin 0 -> 62578 bytes FullHenoc/images/Botones/application.png | Bin 0 -> 464 bytes FullHenoc/images/Botones/application_edit.png | Bin 0 -> 703 bytes FullHenoc/images/Botones/catapulta.png | Bin 0 -> 10059 bytes FullHenoc/images/Botones/catapulta2.jpg | Bin 0 -> 7327 bytes FullHenoc/images/Botones/circle.png | Bin 0 -> 1359 bytes FullHenoc/images/Botones/dr-boton_linea.jpg | Bin 0 -> 776 bytes FullHenoc/images/Botones/editredo.png | Bin 0 -> 1787 bytes FullHenoc/images/Botones/enmarca.png | Bin 0 -> 516 bytes FullHenoc/images/Botones/enmarca2.png | Bin 0 -> 823 bytes FullHenoc/images/Botones/enmarcax.png | Bin 0 -> 27118 bytes FullHenoc/images/Botones/fileclose.png | Bin 0 -> 1121 bytes FullHenoc/images/Botones/fileopen.ico | Bin 0 -> 1406 bytes FullHenoc/images/Botones/fileopen.png | Bin 0 -> 814 bytes FullHenoc/images/Botones/folder.png | Bin 0 -> 3910 bytes FullHenoc/images/Botones/ok.png | Bin 0 -> 979 bytes FullHenoc/images/Botones/picture_empty.png | Bin 0 -> 463 bytes FullHenoc/images/Botones/picture_save.png | Bin 0 -> 755 bytes FullHenoc/images/Botones/rectangle.png | Bin 0 -> 690 bytes FullHenoc/images/Botones/remove.png | Bin 0 -> 1833 bytes FullHenoc/images/Botones/rotateright.png | Bin 0 -> 1732 bytes FullHenoc/images/Botones/save2.png | Bin 0 -> 30492 bytes .../images/Botones/shape_move_backwards.png | Bin 0 -> 358 bytes FullHenoc/images/Botones/shape_square.png | Bin 0 -> 353 bytes FullHenoc/images/Botones/trash.png | Bin 0 -> 66896 bytes FullHenoc/images/Botones/update.png | Bin 0 -> 7890 bytes FullHenoc/images/Botones/wmp.png | Bin 0 -> 55610 bytes FullHenoc/images/background1.png | Bin 0 -> 112 bytes FullHenoc/images/background2.png | Bin 0 -> 114 bytes FullHenoc/images/background3.png | Bin 0 -> 116 bytes FullHenoc/images/background4.png | Bin 0 -> 96 bytes FullHenoc/images/bold.png | Bin 0 -> 274 bytes FullHenoc/images/bringtofront.png | Bin 0 -> 293 bytes FullHenoc/images/delete.png | Bin 0 -> 831 bytes FullHenoc/images/floodfill.png | Bin 0 -> 282 bytes FullHenoc/images/italic.png | Bin 0 -> 247 bytes FullHenoc/images/linecolor.png | Bin 0 -> 145 bytes FullHenoc/images/linepointer.png | Bin 0 -> 141 bytes FullHenoc/images/pointer.png | Bin 0 -> 173 bytes FullHenoc/images/sendtoback.png | Bin 0 -> 318 bytes FullHenoc/images/textpointer.png | Bin 0 -> 753 bytes FullHenoc/images/underline.png | Bin 0 -> 250 bytes FullHenoc/main.cpp | 13 + HenocCameraDrv/GeneraHomografia.cpp | 51 + HenocCameraDrv/HenocMouseDriver/HMouseDrv | Bin 0 -> 98020 bytes .../HenocMouseDriver/HenocMouseDriver.c | 173 ++ HenocCameraDrv/HenocMouseDriver/Makefile | 3 + HenocCameraDrv/HenocMouseDriver/drv-v4l2.h | 178 ++ HenocCameraDrv/HenocMouseDriver/fourcc.h | 23 + HenocCameraDrv/HenocMouseDriver/frame.h | 95 + .../HenocMouseDriver/opencv/.deps/cvutils.Po | 147 + .../HenocMouseDriver/opencv/.deps/ui.Po | 1216 ++++++++ .../HenocMouseDriver/opencv/Makefile | 468 +++ .../HenocMouseDriver/opencv/Makefile.am | 15 + .../HenocMouseDriver/opencv/Makefile.in | 468 +++ .../HenocMouseDriver/opencv/camera-photo.h | 114 + .../HenocMouseDriver/opencv/cv-v4l2.h | 8 + .../HenocMouseDriver/opencv/cvutils.c | 83 + .../HenocMouseDriver/opencv/cvutils.h | 42 + HenocCameraDrv/HenocMouseDriver/opencv/ui.c | 541 ++++ HenocCameraDrv/HenocMouseDriver/opencv/ui.h | 31 + HenocCameraDrv/HenocMouseDriver/v4l2.h | 6 + HenocCameraDrv/HenocMouseDriver/yuv2rgb.h | 11 + HenocCameraDrv/generaHomografia | Bin 0 -> 9197 bytes HenocCameraDrv/uvccapture-0.4/ChangeLog | 21 + HenocCameraDrv/uvccapture-0.4/Makefile | 27 + HenocCameraDrv/uvccapture-0.4/copying | 340 +++ HenocCameraDrv/uvccapture-0.4/readme | 30 + HenocCameraDrv/uvccapture-0.4/todo | 1 + HenocCameraDrv/uvccapture-0.4/uvccapture | Bin 0 -> 22712 bytes HenocCameraDrv/uvccapture-0.4/uvccapture.c | 398 +++ HenocCameraDrv/uvccapture-0.4/v4l2uvc.c | 591 ++++ HenocCameraDrv/uvccapture-0.4/v4l2uvc.h | 78 + HenocUniverse/Makefile | 46 + HenocUniverse/aabb.h | 24 + HenocUniverse/enums.h | 47 + HenocUniverse/henocUniverse.h | 249 ++ HenocUniverse/intersection.h | 64 + HenocUniverse/ode/LICENSE-BSD.TXT | 34 + HenocUniverse/ode/LICENSE.TXT | 502 +++ HenocUniverse/ode/common.h | 322 ++ HenocUniverse/ode/compatibility.h | 40 + HenocUniverse/ode/config.h | 125 + HenocUniverse/ode/contact.h | 90 + HenocUniverse/ode/error.h | 63 + HenocUniverse/ode/mass.h | 107 + HenocUniverse/ode/matrix.h | 194 ++ HenocUniverse/ode/memory.h | 59 + HenocUniverse/ode/misc.h | 85 + HenocUniverse/ode/objects.h | 266 ++ HenocUniverse/ode/ode.h | 44 + HenocUniverse/ode/odecpp.h | 621 ++++ HenocUniverse/ode/odemath.h | 240 ++ HenocUniverse/ode/rotation.h | 70 + HenocUniverse/ode/source/array.cpp | 80 + HenocUniverse/ode/source/array.h | 135 + HenocUniverse/ode/source/error.cpp | 166 + HenocUniverse/ode/source/fastdot.c | 30 + HenocUniverse/ode/source/fastldlt.c | 381 +++ HenocUniverse/ode/source/fastlsolve.c | 298 ++ HenocUniverse/ode/source/fastltsolve.c | 199 ++ HenocUniverse/ode/source/joint.cpp | 2691 +++++++++++++++++ HenocUniverse/ode/source/joint.h | 268 ++ HenocUniverse/ode/source/lcp.cpp | 1474 +++++++++ HenocUniverse/ode/source/lcp.h | 58 + HenocUniverse/ode/source/mass.cpp | 313 ++ HenocUniverse/ode/source/mat.cpp | 230 ++ HenocUniverse/ode/source/mat.h | 71 + HenocUniverse/ode/source/matrix.cpp | 360 +++ HenocUniverse/ode/source/memory.cpp | 88 + HenocUniverse/ode/source/misc.cpp | 147 + HenocUniverse/ode/source/objects.h | 125 + HenocUniverse/ode/source/obstack.cpp | 130 + HenocUniverse/ode/source/obstack.h | 68 + HenocUniverse/ode/source/ode.cpp | 1538 ++++++++++ HenocUniverse/ode/source/odemath.cpp | 156 + HenocUniverse/ode/source/quickstep.cpp | 671 ++++ HenocUniverse/ode/source/quickstep.h | 33 + HenocUniverse/ode/source/rotation.cpp | 304 ++ HenocUniverse/ode/source/stack.h | 138 + HenocUniverse/ode/source/step.cpp | 1001 ++++++ HenocUniverse/ode/source/step.h | 36 + HenocUniverse/ode/source/stepfast.cpp | 1138 +++++++ HenocUniverse/ode/source/testing.cpp | 243 ++ HenocUniverse/ode/source/testing.h | 65 + HenocUniverse/ode/source/timer.cpp | 399 +++ HenocUniverse/ode/source/util.cpp | 279 ++ HenocUniverse/ode/source/util.h | 38 + HenocUniverse/ode/timer.h | 76 + HenocUniverse/shapes.h | 110 + HenocUniverse/source/circle-circle.cpp | 47 + HenocUniverse/source/composite-all.cpp | 94 + HenocUniverse/source/henocUniverse.cpp | 275 ++ HenocUniverse/source/intersection.cpp | 41 + HenocUniverse/source/quad-circle.cpp | 102 + HenocUniverse/source/quad-quad.cpp | 300 ++ HenocUniverse/source/shapes.cpp | 206 ++ HenocUniverse/source/terrain-all.cpp | 89 + HenocUniverse/source/vector.cpp | 39 + HenocUniverse/vector.h | 46 + autoclean.sh | 33 + autohenoc.sh | 52 + 175 files changed, 27045 insertions(+) create mode 100755 FullHenoc/.DS_Store create mode 100755 FullHenoc/CfrmHenoc.cpp create mode 100755 FullHenoc/CfrmHenoc.h create mode 100755 FullHenoc/CfrmMundo.cpp create mode 100755 FullHenoc/CfrmMundo.h create mode 100755 FullHenoc/CfrmPeCaLi.cpp create mode 100755 FullHenoc/CfrmPeCaLi.h create mode 100755 FullHenoc/HBall.cpp create mode 100755 FullHenoc/HBall.h create mode 100755 FullHenoc/HBox.cpp create mode 100755 FullHenoc/HBox.h create mode 100755 FullHenoc/HLine.cpp create mode 100755 FullHenoc/HLine.h create mode 100755 FullHenoc/HObject.cpp create mode 100755 FullHenoc/HObject.h create mode 100755 FullHenoc/Henoc.pro create mode 100755 FullHenoc/diagramscene.cpp create mode 100755 FullHenoc/diagramscene.h create mode 100755 FullHenoc/diagramscene.pro create mode 100755 FullHenoc/diagramscene.qrc create mode 100755 FullHenoc/frmCatapulta.ui create mode 100755 FullHenoc/frmHenoc.ui create mode 100755 FullHenoc/frmMundo.ui create mode 100755 FullHenoc/frmPeCaLi.ui create mode 100755 FullHenoc/glwidget.cpp create mode 100755 FullHenoc/glwidget.h create mode 100755 FullHenoc/henocUniverseI.cpp create mode 100755 FullHenoc/henocUniverseI.h create mode 100755 FullHenoc/images/Botones/Config.png create mode 100755 FullHenoc/images/Botones/Enoch21.JPG create mode 100755 FullHenoc/images/Botones/Folder-Secure.png create mode 100755 FullHenoc/images/Botones/My-Documents.png create mode 100755 FullHenoc/images/Botones/SSave.png create mode 100755 FullHenoc/images/Botones/Symbol-Stop.png create mode 100755 FullHenoc/images/Botones/application.png create mode 100755 FullHenoc/images/Botones/application_edit.png create mode 100755 FullHenoc/images/Botones/catapulta.png create mode 100755 FullHenoc/images/Botones/catapulta2.jpg create mode 100755 FullHenoc/images/Botones/circle.png create mode 100755 FullHenoc/images/Botones/dr-boton_linea.jpg create mode 100755 FullHenoc/images/Botones/editredo.png create mode 100755 FullHenoc/images/Botones/enmarca.png create mode 100755 FullHenoc/images/Botones/enmarca2.png create mode 100755 FullHenoc/images/Botones/enmarcax.png create mode 100755 FullHenoc/images/Botones/fileclose.png create mode 100755 FullHenoc/images/Botones/fileopen.ico create mode 100755 FullHenoc/images/Botones/fileopen.png create mode 100755 FullHenoc/images/Botones/folder.png create mode 100755 FullHenoc/images/Botones/ok.png create mode 100755 FullHenoc/images/Botones/picture_empty.png create mode 100755 FullHenoc/images/Botones/picture_save.png create mode 100755 FullHenoc/images/Botones/rectangle.png create mode 100755 FullHenoc/images/Botones/remove.png create mode 100755 FullHenoc/images/Botones/rotateright.png create mode 100755 FullHenoc/images/Botones/save2.png create mode 100755 FullHenoc/images/Botones/shape_move_backwards.png create mode 100755 FullHenoc/images/Botones/shape_square.png create mode 100755 FullHenoc/images/Botones/trash.png create mode 100755 FullHenoc/images/Botones/update.png create mode 100755 FullHenoc/images/Botones/wmp.png create mode 100755 FullHenoc/images/background1.png create mode 100755 FullHenoc/images/background2.png create mode 100755 FullHenoc/images/background3.png create mode 100755 FullHenoc/images/background4.png create mode 100755 FullHenoc/images/bold.png create mode 100755 FullHenoc/images/bringtofront.png create mode 100755 FullHenoc/images/delete.png create mode 100755 FullHenoc/images/floodfill.png create mode 100755 FullHenoc/images/italic.png create mode 100755 FullHenoc/images/linecolor.png create mode 100755 FullHenoc/images/linepointer.png create mode 100755 FullHenoc/images/pointer.png create mode 100755 FullHenoc/images/sendtoback.png create mode 100755 FullHenoc/images/textpointer.png create mode 100755 FullHenoc/images/underline.png create mode 100755 FullHenoc/main.cpp create mode 100755 HenocCameraDrv/GeneraHomografia.cpp create mode 100755 HenocCameraDrv/HenocMouseDriver/HMouseDrv create mode 100755 HenocCameraDrv/HenocMouseDriver/HenocMouseDriver.c create mode 100755 HenocCameraDrv/HenocMouseDriver/Makefile create mode 100755 HenocCameraDrv/HenocMouseDriver/drv-v4l2.h create mode 100755 HenocCameraDrv/HenocMouseDriver/fourcc.h create mode 100755 HenocCameraDrv/HenocMouseDriver/frame.h create mode 100755 HenocCameraDrv/HenocMouseDriver/opencv/.deps/cvutils.Po create mode 100755 HenocCameraDrv/HenocMouseDriver/opencv/.deps/ui.Po create mode 100755 HenocCameraDrv/HenocMouseDriver/opencv/Makefile create mode 100755 HenocCameraDrv/HenocMouseDriver/opencv/Makefile.am create mode 100755 HenocCameraDrv/HenocMouseDriver/opencv/Makefile.in create mode 100755 HenocCameraDrv/HenocMouseDriver/opencv/camera-photo.h create mode 100755 HenocCameraDrv/HenocMouseDriver/opencv/cv-v4l2.h create mode 100755 HenocCameraDrv/HenocMouseDriver/opencv/cvutils.c create mode 100755 HenocCameraDrv/HenocMouseDriver/opencv/cvutils.h create mode 100755 HenocCameraDrv/HenocMouseDriver/opencv/ui.c create mode 100755 HenocCameraDrv/HenocMouseDriver/opencv/ui.h create mode 100755 HenocCameraDrv/HenocMouseDriver/v4l2.h create mode 100755 HenocCameraDrv/HenocMouseDriver/yuv2rgb.h create mode 100755 HenocCameraDrv/generaHomografia create mode 100755 HenocCameraDrv/uvccapture-0.4/ChangeLog create mode 100755 HenocCameraDrv/uvccapture-0.4/Makefile create mode 100755 HenocCameraDrv/uvccapture-0.4/copying create mode 100755 HenocCameraDrv/uvccapture-0.4/readme create mode 100755 HenocCameraDrv/uvccapture-0.4/todo create mode 100755 HenocCameraDrv/uvccapture-0.4/uvccapture create mode 100755 HenocCameraDrv/uvccapture-0.4/uvccapture.c create mode 100755 HenocCameraDrv/uvccapture-0.4/v4l2uvc.c create mode 100755 HenocCameraDrv/uvccapture-0.4/v4l2uvc.h create mode 100755 HenocUniverse/Makefile create mode 100755 HenocUniverse/aabb.h create mode 100755 HenocUniverse/enums.h create mode 100755 HenocUniverse/henocUniverse.h create mode 100755 HenocUniverse/intersection.h create mode 100755 HenocUniverse/ode/LICENSE-BSD.TXT create mode 100755 HenocUniverse/ode/LICENSE.TXT create mode 100755 HenocUniverse/ode/common.h create mode 100755 HenocUniverse/ode/compatibility.h create mode 100755 HenocUniverse/ode/config.h create mode 100755 HenocUniverse/ode/contact.h create mode 100755 HenocUniverse/ode/error.h create mode 100755 HenocUniverse/ode/mass.h create mode 100755 HenocUniverse/ode/matrix.h create mode 100755 HenocUniverse/ode/memory.h create mode 100755 HenocUniverse/ode/misc.h create mode 100755 HenocUniverse/ode/objects.h create mode 100755 HenocUniverse/ode/ode.h create mode 100755 HenocUniverse/ode/odecpp.h create mode 100755 HenocUniverse/ode/odemath.h create mode 100755 HenocUniverse/ode/rotation.h create mode 100755 HenocUniverse/ode/source/array.cpp create mode 100755 HenocUniverse/ode/source/array.h create mode 100755 HenocUniverse/ode/source/error.cpp create mode 100755 HenocUniverse/ode/source/fastdot.c create mode 100755 HenocUniverse/ode/source/fastldlt.c create mode 100755 HenocUniverse/ode/source/fastlsolve.c create mode 100755 HenocUniverse/ode/source/fastltsolve.c create mode 100755 HenocUniverse/ode/source/joint.cpp create mode 100755 HenocUniverse/ode/source/joint.h create mode 100755 HenocUniverse/ode/source/lcp.cpp create mode 100755 HenocUniverse/ode/source/lcp.h create mode 100755 HenocUniverse/ode/source/mass.cpp create mode 100755 HenocUniverse/ode/source/mat.cpp create mode 100755 HenocUniverse/ode/source/mat.h create mode 100755 HenocUniverse/ode/source/matrix.cpp create mode 100755 HenocUniverse/ode/source/memory.cpp create mode 100755 HenocUniverse/ode/source/misc.cpp create mode 100755 HenocUniverse/ode/source/objects.h create mode 100755 HenocUniverse/ode/source/obstack.cpp create mode 100755 HenocUniverse/ode/source/obstack.h create mode 100755 HenocUniverse/ode/source/ode.cpp create mode 100755 HenocUniverse/ode/source/odemath.cpp create mode 100755 HenocUniverse/ode/source/quickstep.cpp create mode 100755 HenocUniverse/ode/source/quickstep.h create mode 100755 HenocUniverse/ode/source/rotation.cpp create mode 100755 HenocUniverse/ode/source/stack.h create mode 100755 HenocUniverse/ode/source/step.cpp create mode 100755 HenocUniverse/ode/source/step.h create mode 100755 HenocUniverse/ode/source/stepfast.cpp create mode 100755 HenocUniverse/ode/source/testing.cpp create mode 100755 HenocUniverse/ode/source/testing.h create mode 100755 HenocUniverse/ode/source/timer.cpp create mode 100755 HenocUniverse/ode/source/util.cpp create mode 100755 HenocUniverse/ode/source/util.h create mode 100755 HenocUniverse/ode/timer.h create mode 100755 HenocUniverse/shapes.h create mode 100755 HenocUniverse/source/circle-circle.cpp create mode 100755 HenocUniverse/source/composite-all.cpp create mode 100755 HenocUniverse/source/henocUniverse.cpp create mode 100755 HenocUniverse/source/intersection.cpp create mode 100755 HenocUniverse/source/quad-circle.cpp create mode 100755 HenocUniverse/source/quad-quad.cpp create mode 100755 HenocUniverse/source/shapes.cpp create mode 100755 HenocUniverse/source/terrain-all.cpp create mode 100755 HenocUniverse/source/vector.cpp create mode 100755 HenocUniverse/vector.h create mode 100644 autoclean.sh create mode 100644 autohenoc.sh diff --git a/FullHenoc/.DS_Store b/FullHenoc/.DS_Store new file mode 100755 index 0000000000000000000000000000000000000000..8bdd8ae15b42328ecbfb133779be952968aeeac6 GIT binary patch literal 15364 zcmeHNO-~$05Uu9R3k&NFvVGo2i9{|I#PTuH3a}KcSSE^ci4rBS5FlPNunTrBk+?W_ zAN>P-^bg3*^4&kcNB;mHb536M3_DfbJ+p9$gj0=Xdg<*~_3CX;Pglz>eZgm5uyb3dsJlgdme=?O#pO`cIn)&?hRfk-?-c#I zMSYgnIh>sL3_FEqS7a#8dq;c6cPH1)Ydf0)ra-K~=V)s9+&e=~eVcl8L`NHanSHyK z%D#zaMF0Ju@0*&w*cFg=eOCW=l+_bW!|pB}Qbs-9(=(7gXUm@8yp`bm8gbncwl(62 z2E0ch**In{eJm&+9syq&kb4?QKSIJc!q+10pT|MpBc20#g8v6J#CZcY5*o7>(_aW1 z?J+h&-SHbKJjeSDJRibm(j&*K5WE9ZA_DuK-!s8a_YSpWHnMjUwja_q{RTEYI;mkK zQ5=ItrC7ZetYSopnMBM$qsJIQdtf%yzpGbB_MP*34#EF?pKC|~BZtVn4V9cX{7@yu5unnmoT9YD3wRS}IQjD2l`{VN*{A03Z_%2A;D&xrnk zcAno0!LLCNpu{6Y^ecGwX*a<|_WuO7ac$GjvkQ-QRu~p{U)?qgNA(FB^j{h*S-}ow8z*8IUoXszcIq^z$cIU z0nU#xHhbVwJ6k9_Wh~Wp0xmw5SIJoRFnXSX&yK$itL6ueMdDkdSYmwOn|}+1y+Ox_ zoX27{|B89KF)LzNynvmM3*t~V7BC7Az$CsV!r28;)F9%|qEaj+sx?wXuY9X`O6+_EHpTRVU|NMsah16Cv&h}zg-ybbGLZTns_O=HxYM+43YOfGOD@CYETuk4{NMYtW)W0>@Lrw6FghUyN`V~ zmhbiqz?k*2k1QGpS_A;N48*s}agE;}l9> ztucz3O4NMeOJA)Kj_0t+zXZp5>?d4sXF=iS61d=6;W0246I>(AZ|bJt$NQD#TW_h^ z2l^?5eorpJsq8Ixx7oAG;rZ?|jRR$gpa0*he*e#RvXd!b3VfUjxT*E;)>jc*@oYVc zasn+K9KYb;KCf|b!DkP_iM#MW{&75N|2Q7?GPiTV=WbD-# literal 0 HcmV?d00001 diff --git a/FullHenoc/CfrmHenoc.cpp b/FullHenoc/CfrmHenoc.cpp new file mode 100755 index 0000000..57bf889 --- /dev/null +++ b/FullHenoc/CfrmHenoc.cpp @@ -0,0 +1,495 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include"glwidget.h" +#include + +using namespace HenocUniverseI; +using namespace std; + +CfrmHenoc::CfrmHenoc( QWidget * parent, Qt::WFlags flags):QMainWindow(parent, flags){ + setupUi(this); + + connect(btnBox, SIGNAL(clicked()), this, SLOT(AddBox())); + connect(btnLine, SIGNAL(clicked()), this, SLOT(AddLine())); + connect(btnCatapult, SIGNAL(clicked()), this, SLOT(AddCatapult())); + connect(btnBall, SIGNAL(clicked()), this, SLOT(AddBall())); + connect(btnPlay, SIGNAL(clicked()), this, SLOT(Play())); + connect(btnPropiedades, SIGNAL(clicked()), this, SLOT(changeProperties())); + connect(btnBorrar, SIGNAL(clicked()), this, SLOT(Delete())); + connect(bntStop, SIGNAL(clicked()), this, SLOT(Stop())); + connect(btnSave, SIGNAL(clicked()), this, SLOT(Save())); + connect(btnFrame, SIGNAL(clicked()), this, SLOT(Open())); + + myWorldProp.fAnim = 3; + myWorldProp.delta = 0.15f; + myWorldProp.cmv = 1; + myWorldProp.fcs = 0.01f; + myWorldProp.gravity = 0.5f; + myWorldProp.erp = 0.7f; + myWorldProp.lin = 0.25f; + myWorldProp.co = 0; + + scene = new DiagramScene(NULL); + scene->setSceneRect(QRectF(0, 0, 655, 517)); + view->setScene(scene); + + connect(scene, SIGNAL(itemInserted(QGraphicsItem *)),this, SLOT(itemInserted(QGraphicsItem *))); + + QGridLayout *gridLayoutx; + gridLayoutx = new QGridLayout(page_2); + glWidget = new GLWidget(page_2); + gridLayoutx->addWidget(glWidget, 0, 0, 1 ,1); + + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), glWidget, SLOT(animate())); +} + +void CfrmHenoc::Play(){ + QList lista = scene->items(); + int xa, ya, wa, ha; + glWidget->delSpace(); + for(unsigned int i=0; i < lista.size(); i++){ + lista.at(i); + xa = view->mapFromScene( lista.at(i)->mapToScene( lista.at(i)->boundingRect() )).boundingRect().x(); + ya = view->mapFromScene( lista.at(i)->mapToScene( lista.at(i)->boundingRect() )).boundingRect().y(); + wa = view->mapFromScene( lista.at(i)->mapToScene( lista.at(i)->boundingRect() )).boundingRect().width(); + ha = view->mapFromScene( lista.at(i)->mapToScene( lista.at(i)->boundingRect() )).boundingRect().height(); + + QGraphicsRectItem * auxR = qgraphicsitem_cast(lista.at(i)); + QGraphicsEllipseItem * auxE = qgraphicsitem_cast(lista.at(i)); + QGraphicsLineItem * auxL = qgraphicsitem_cast(lista.at(i)); + + QRect fao(xa,ya,wa,ha); + + if(0 != auxR){ + HObject obj = ((HBox*)auxR)->obj; + glWidget->addBox(fao.center().x(), fao.center().y(), obj.width(), obj.height(), fabsf(obj.getMass()), obj.getFriction(), obj.getBounceFactor(), obj.getBounceVelocity(), obj.getColMask(), obj.getFrictionMask(), obj.getRotation(), obj.getColor()); + } + else if(0 != auxE){ + div_t q; + q = div(ha,2); + HObject obj = ((HBall*)auxE)->obj; + glWidget->addBall(fao.center().x(), fao.center().y(), q.quot, fabsf(obj.getMass()), obj.getFriction(), obj.getBounceFactor(), obj.getBounceVelocity(), obj.getColMask(), obj.getFrictionMask(), obj.getRotation(), obj.getColor()); + } + else if(0 != auxL){ + HObject obj = ((HLine*)auxL)->obj; + glWidget->addLine((int)auxL->line().x1(), (int)auxL->line().y1(), (int)auxL->line().x2(), (int)auxL->line().y2(), obj.getFriction(), obj.getColMask(), obj.getFrictionMask(), obj.getColor() ); + //glWidget->addLine(xa, ya, xa+wa, ya+ha); + } + + } + + + + stackedWidget->setCurrentIndex(1); + glWidget->setWorldParams(myWorldProp); + timer->start( myWorldProp.fAnim ); +} + +void CfrmHenoc::AddBox(){ + scene->setMode(DiagramScene::InsertItem); + scene->setItemType(1); + btnBox->setChecked(true); +} + +void CfrmHenoc::AddLine(){ + scene->setMode(DiagramScene::InsertItem); + scene->setItemType(3); + btnBox->setChecked(true); +} + +void CfrmHenoc::AddCatapult(){ + { + QLineF ml( 0, 0, 655, 0 ); + HLine *item = new HLine(ml); + item->setFlag(QGraphicsItem::ItemIsSelectable, false); + float tColor = (0); + item->obj.setColor( tColor ); + ((QGraphicsLineItem*)item)->setPen(QPen(fromThetaColor(tColor), 1)); + scene->addItem(item); + item->obj.setType(3); + item->obj.setFriction( 99 ); + item->obj.setColMask( 0 ); + item->obj.setFrictionMask( 0 ); + } + + { + QLineF ml( 655, 0, 655, 517 ); + HLine *item = new HLine(ml); + item->setFlag(QGraphicsItem::ItemIsSelectable, false); + float tColor = (0); + item->obj.setColor( tColor ); + ((QGraphicsLineItem*)item)->setPen(QPen(fromThetaColor(tColor), 1)); + scene->addItem(item); + item->obj.setType(3); + item->obj.setFriction( 99 ); + item->obj.setColMask( 0 ); + item->obj.setFrictionMask( 0 ); + } + + { + QLineF ml( 0, 517, 655, 517 ); + HLine *item = new HLine(ml); + item->setFlag(QGraphicsItem::ItemIsSelectable, false); + float tColor = (0); + item->obj.setColor( tColor ); + ((QGraphicsLineItem*)item)->setPen(QPen(fromThetaColor(tColor), 1)); + scene->addItem(item); + item->obj.setType(3); + item->obj.setFriction( 99 ); + item->obj.setColMask( 0 ); + item->obj.setFrictionMask( 0 ); + } + + { + QLineF ml( 0, 0, 0, 517 ); + HLine *item = new HLine(ml); + item->setFlag(QGraphicsItem::ItemIsSelectable, false); + float tColor = (0); + item->obj.setColor( tColor ); + ((QGraphicsLineItem*)item)->setPen(QPen(fromThetaColor(tColor), 1)); + scene->addItem(item); + item->obj.setType(3); + item->obj.setFriction( 99 ); + item->obj.setColMask( 0 ); + item->obj.setFrictionMask( 0 ); + } +} + +void CfrmHenoc::AddBall(){ + scene->setMode(DiagramScene::InsertItem); + scene->setItemType(2); + btnBox->setChecked(true); +} + +void CfrmHenoc::itemInserted(QGraphicsItem *){ + scene->setMode(DiagramScene::MoveItem); + btnBox->setChecked(false); + scene->setItemType(0); +} + +void CfrmHenoc::changeProperties(){ + QList lista = scene->selectedItems(); + + if(0 == lista.size()){ + CfrmMundo dialog(&myWorldProp, this); + if( QDialog::Accepted == dialog.exec() ){ + } + return; + } + + if(lista.size() != 1){ + QMessageBox::warning(this, tr("Henoc"),tr("Error, solo 1 elemento puede estar seleccionado")); + return; + } + + CfrmPeCaLi dialog((HObject*)(&(((HBox*)lista.at(0))->obj)),this); + if( QDialog::Accepted == dialog.exec() ){ + if( 1 == ((HBox*)lista.at(0))->obj.getType() ){ + QRectF fao = ((QGraphicsRectItem*)lista.at(0))->rect(); + fao.setHeight( ((HBox*)lista.at(0))->obj.height() ); + fao.setWidth( ((HBox*)lista.at(0))->obj.width() ); + ((QGraphicsRectItem*)lista.at(0))->setRect(fao); + ((QGraphicsRectItem*)lista.at(0))->resetMatrix(); + QMatrix xx = ((QGraphicsRectItem*)lista.at(0))->matrix( ); + ((QGraphicsRectItem*)lista.at(0))->moveBy(-((QGraphicsRectItem*)lista.at(0))->rect().width()/2,-((QGraphicsRectItem*)lista.at(0))->rect().height()/2); + ((QGraphicsRectItem*)lista.at(0))->rotate( (((HBox*)lista.at(0))->obj.getRotation()) ); + ((QGraphicsRectItem*)lista.at(0))->moveBy(((QGraphicsRectItem*)lista.at(0))->rect().width()/2,((QGraphicsRectItem*)lista.at(0))->rect().height()/2); + } + else{ + QRectF fao = ((QGraphicsEllipseItem*)lista.at(0))->rect(); + fao.setHeight( ((HBox*)lista.at(0))->obj.height() ); + fao.setWidth( ((HBox*)lista.at(0))->obj.width() ); + ((QGraphicsEllipseItem*)lista.at(0))->setRect(fao); + } + } +} + +void CfrmHenoc::Delete(){ + foreach (QGraphicsItem *item, scene->selectedItems()) { + scene->removeItem(item); + } +} + +void CfrmHenoc::Stop(){ + timer->stop(); + stackedWidget->setCurrentIndex(0); +} + + +void CfrmHenoc::Save(){ + QString fileName = QFileDialog::getSaveFileName(this, tr("Guardar xki"), + QDir::currentPath() + "/demo01.xki", + tr("eXtended sKetch sImulation (*.xki)")); + + if ( fileName.isEmpty() ) return; + + QFile file(fileName); + if (!file.open(QFile::WriteOnly | QFile::Text)) { + QMessageBox::warning(this, tr("XKI"), + tr("No se pudo escribir en archivo %1:\n%2.") + .arg(fileName) + .arg(file.errorString())); + return; + } + + QDomDocument doc("XKI"); + QDomElement root = doc.createElement("Sketch"); + doc.appendChild(root); + + QList lista = scene->items(); + int xa, ya, wa, ha; + glWidget->delSpace(); + for(unsigned int i=0; i < lista.size(); i++){ + xa = ( lista.at(i)->mapToScene( lista.at(i)->boundingRect() )).boundingRect().x(); + ya = ( lista.at(i)->mapToScene( lista.at(i)->boundingRect() )).boundingRect().y(); + wa = ( lista.at(i)->mapToScene( lista.at(i)->boundingRect() )).boundingRect().width(); + ha = ( lista.at(i)->mapToScene( lista.at(i)->boundingRect() )).boundingRect().height(); + + QGraphicsRectItem * auxR = qgraphicsitem_cast(lista.at(i)); + QGraphicsEllipseItem * auxE = qgraphicsitem_cast(lista.at(i)); + QGraphicsLineItem * auxL = qgraphicsitem_cast(lista.at(i)); + + QRect fao(xa,ya,wa,ha); + + if(0 != auxR){ + QDomElement tag = doc.createElement("Box"); + HObject obj = ((HBox*)auxR)->obj; + XkiAddBox(tag, xa, ya, obj.width(), obj.height(), + obj.getMass(), obj.getFriction(), obj.getBounceFactor(), + obj.getBounceVelocity(), obj.getColMask(), + obj.getFrictionMask(), obj.getRotation()); + root.appendChild(tag); + } + else if(0 != auxE){ + div_t q; + q = div(ha,2); + HObject obj = ((HBall*)auxE)->obj; + QDomElement tag = doc.createElement("Ball"); + XkiAddBall(tag, xa, ya, wa, + obj.getMass(), obj.getFriction(), obj.getBounceFactor(), + obj.getBounceVelocity(), obj.getColMask(), + obj.getFrictionMask(), obj.getRotation()); + root.appendChild(tag); + } + else if(0 != auxL){ + QDomElement tag = doc.createElement("Line"); + HObject obj = ((HLine*)auxL)->obj; + XkiAddLine(tag, (int)auxL->line().x1(), (int)auxL->line().y1(), + (int)auxL->line().x2(), (int)auxL->line().y2(), + obj.getFriction(), obj.getColMask(), obj.getFrictionMask() ); + root.appendChild(tag); + } + + } + + QIODevice *device = &file; + QTextStream out(device); + doc.save(out, 4); + file.close(); +} + +void CfrmHenoc::Open(){ + QString fileName = QFileDialog::getOpenFileName(this, tr("Abrir XKI"), + QDir::currentPath() + "/demo01.xki", + tr("eXtended sKetch sImulation (*.xki)")); + + if (fileName.isEmpty()) return; + + QFile file(fileName); + if (!file.open(QFile::ReadOnly | QFile::Text)) { + QMessageBox::warning(this, tr("XKI"), + tr("No se pudo leer %1:\n%2.") + .arg(fileName) + .arg(file.errorString())); + return; + } + + QIODevice *device = &file; + QString errorStr; + int errorLine; + int errorColumn; + QDomDocument domDocument; + + if (!domDocument.setContent(device, true, &errorStr, &errorLine, &errorColumn)) { + QMessageBox::information(window(), tr("XKI"), + tr("Error de parser en linea %1, columna %2:\n%3") + .arg(errorLine) + .arg(errorColumn) + .arg(errorStr)); + return; + } + + + //Limpia la escena + foreach (QGraphicsItem *item, scene->items()) { + scene->removeItem(item); + } + + QDomElement root = domDocument.documentElement(); + + QDomNodeList balls = root.elementsByTagName("Ball"); + QDomNodeList lines = root.elementsByTagName("Line"); + QDomNodeList boxes = root.elementsByTagName("Box"); + + int i = 0; + + for(i = 0; i < balls.count(); i++){ + QDomElement e = balls.at(i).toElement(); + HBall *item = new HBall( e.attribute("x").toInt(), e.attribute("y").toInt(), + e.attribute("radius").toInt(), e.attribute("radius").toInt() ); + item->setFlag(QGraphicsItem::ItemIsSelectable, true); + scene->addItem(item); + item->obj.setType(2); + item->obj.setWidth( e.attribute("radius").toInt() ); + item->obj.setHeight( e.attribute("radius").toInt() ); + item->obj.setMass( e.attribute("mass").toFloat() ); + item->obj.setFriction( e.attribute("friction").toFloat() ); + item->obj.setBounceFactor( e.attribute("bounceFactor").toFloat() ); + item->obj.setBounceVelocity( e.attribute("bounceVelocity").toFloat() ); + item->obj.setColMask( e.attribute("collisionMask").toFloat() ); + item->obj.setFrictionMask( e.attribute("frictionMask").toFloat() ); + item->obj.setRotation( e.attribute("rotation").toFloat() ); + float tColor = (60.0f + (rand() % 90) - 45); + item->obj.setColor( tColor ); + ((QGraphicsEllipseItem*)(item))->setPen( QPen( fromThetaColor(tColor) ) ); + ((QGraphicsEllipseItem*)(item))->setBrush(fromThetaIncreasedColor(tColor, 0.5)); + } + + for(i = 0; i < boxes.size(); i++){ + QDomElement e = boxes.at(i).toElement(); + //HBox *item = new HBox( e.attribute("x").toInt(), e.attribute("y").toInt(), + // e.attribute("width").toInt(), e.attribute("height").toInt() ); + + HBox *item = new HBox( 10, 10, + 20, 20 ); + item->setFlag(QGraphicsItem::ItemIsSelectable, true); + item->obj.setType(1); + item->obj.setWidth( e.attribute("width").toInt() ); + item->obj.setHeight( e.attribute("height").toInt() ); + item->obj.setMass( e.attribute("mass").toFloat() ); + item->obj.setFriction( e.attribute("friction").toFloat() ); + item->obj.setBounceFactor( e.attribute("bounceFactor").toFloat() ); + item->obj.setBounceVelocity( e.attribute("bounceVelocity").toFloat() ); + item->obj.setColMask( e.attribute("collisionMask").toFloat() ); + item->obj.setFrictionMask( e.attribute("frictionMask").toFloat() ); + item->obj.setRotation( e.attribute("rotation").toFloat() ); + float tColor = (60.0f + (rand() % 90) - 45); + + QRectF dao(e.attribute("x").toInt(), e.attribute("y").toInt(), + e.attribute("width").toInt(), e.attribute("height").toInt() ); + + item->obj.setColor( tColor ); + + ((QGraphicsRectItem*)(item))->setPen( QPen( fromThetaColor(tColor) ) ); + ((QGraphicsRectItem*)(item))->setBrush(fromThetaIncreasedColor(tColor, 0.5)); + ((QGraphicsRectItem*)(item))->setFlag(QGraphicsItem::ItemIsSelectable, true); + ((QGraphicsRectItem*)(item))->setRect( dao); + // ((QGraphicsRectItem*)item)->moveBy(-((QGraphicsRectItem*)item)->rect().width()/2,-((QGraphicsRectItem*)item)->rect().height()/2); + ((QGraphicsRectItem*)item)->rotate( (((HBox*)item)->obj.getRotation()) ); + // ((QGraphicsRectItem*)item)->moveBy(((QGraphicsRectItem*)item)->rect().width()/2,((QGraphicsRectItem*)item)->rect().height()/2); + scene->addItem(((QGraphicsRectItem*)item)); + + QRectF fao = ((QGraphicsRectItem*)item)->rect(); + fao.setHeight( item->obj.height() ); + fao.setWidth( item->obj.width() ); + + int auxx=0, auxy=0, xao, yao; + for(xao=0; xao < 1600; xao++){ + auxx = (((QGraphicsRectItem*)(item))->mapToScene( QPointF(xao,10) )).x(); + if(auxx == e.attribute("x").toInt()){ + break; + } + } + for(yao=0; yao < 1600; yao++){ + auxy = (((QGraphicsRectItem*)(item))->mapToScene( QPointF(10,yao) )).y(); + if(auxy == e.attribute("y").toInt()){ + break; + } + } + + float ix = (((QGraphicsRectItem*)(item))->mapToScene( ((QGraphicsRectItem*)(item))->boundingRect() )).boundingRect().x(); + float iy = (((QGraphicsRectItem*)(item))->mapToScene( ((QGraphicsRectItem*)(item))->boundingRect() )).boundingRect().y(); + + float dx = auxx - ((auxx-e.attribute("x").toInt())+ix); + float dy = auxy - ((auxy-e.attribute("y").toInt())+iy); + ((QGraphicsRectItem*)item)->setPos(dx,dy); + + } + + for(i = 0; i < lines.size(); i++){ + QDomElement e = lines.at(i).toElement(); + QLineF ml( (qreal)e.attribute("x1").toInt(), (qreal)e.attribute("y1").toInt(), + (qreal)e.attribute("x2").toInt(), (qreal)e.attribute("y2").toInt() ); + HLine *item = new HLine(ml); + item->setFlag(QGraphicsItem::ItemIsSelectable, true); + float tColor = (60.0f + (rand() % 90) - 45); + item->obj.setColor( tColor ); + ((QGraphicsLineItem*)item)->setPen(QPen(fromThetaColor(tColor), 2)); + scene->addItem(item); + item->obj.setType(3); + item->obj.setFriction( e.attribute("friction").toFloat() ); + item->obj.setColMask( e.attribute("collisionMask").toFloat() ); + item->obj.setFrictionMask( e.attribute("frictionMask").toFloat() ); + } + file.close(); + //QLineF newLine(((QGraphicsLineItem*)itemx)->line().p1(), mouseEvent->scenePos()); + +} + +void CfrmHenoc::XkiAddLine(QDomElement &element, int x1, int y1, int x2, int y2, float friction, int colMask, int frictionMask){ + element.setAttribute("x1",x1); + element.setAttribute("x2",x2); + element.setAttribute("y1",y1); + element.setAttribute("y2",y2); + element.setAttribute("friction",friction); + element.setAttribute("collisionMask",colMask); + element.setAttribute("frictionMask",frictionMask); +} + +void CfrmHenoc::XkiAddBox(QDomElement &element, int x, int y, int w, int h, + float mass, float friction, float bounceFactor, float bounceVelocity, + int colMask, int frictionMask, int rotation){ + + element.setAttribute("x",x); + element.setAttribute("y",y); + element.setAttribute("width",w); + element.setAttribute("height",h); + element.setAttribute("mass",mass); + element.setAttribute("friction",friction); + element.setAttribute("bounceFactor",bounceFactor); + element.setAttribute("bounceVelocity",bounceVelocity); + element.setAttribute("collisionMask",colMask); + element.setAttribute("frictionMask",frictionMask); + element.setAttribute("rotation",rotation); +} + +void CfrmHenoc::XkiAddBall(QDomElement &element, int x, int y, int r, + float mass, float friction, float bounceFactor, + float bounceVelocity, int colMask, int frictionMask, int rotation){ + + element.setAttribute("x",x); + element.setAttribute("y",y); + element.setAttribute("radius",r); + element.setAttribute("mass",mass); + element.setAttribute("friction",friction); + element.setAttribute("bounceFactor",bounceFactor); + element.setAttribute("bounceVelocity",bounceVelocity); + element.setAttribute("collisionMask",colMask); + element.setAttribute("frictionMask",frictionMask); + element.setAttribute("rotation",rotation); + +} diff --git a/FullHenoc/CfrmHenoc.h b/FullHenoc/CfrmHenoc.h new file mode 100755 index 0000000..fe86d98 --- /dev/null +++ b/FullHenoc/CfrmHenoc.h @@ -0,0 +1,46 @@ +#ifndef __CFRMHENOC_H__ +#define __CFRMHENOC_H__ + +#include "ui_frmHenoc.h" +#include +#include +#include +#include + + +using namespace std; + +using namespace Ui; + +class DiagramScene; +class QGraphicsItem; +class GLWidget; +class QTimer; + +class CfrmHenoc : public QMainWindow, frmHenoc { + Q_OBJECT + public: + CfrmHenoc(QWidget * parent = 0, Qt::WFlags flags = 0 ); + public slots: + void changeProperties(); + void Delete(); + void Stop(); + void Save(); + void Open(); + void Play(); + void AddBox(); + void AddLine(); + void AddCatapult(); + void AddBall(); + void itemInserted(QGraphicsItem *); + private: + void XkiAddLine(QDomElement &, int x1, int y1, int x2, int y2, float friction, int colMask, int frictionMask); + void XkiAddBox(QDomElement &, int x, int y, int w, int h, float mass, float friction, float bounceFactor, float bounceVelocity, int colMask, int frictionMask, int rotation); + void XkiAddBall(QDomElement &, int x, int y, int r, float mass, float friction, float bounceFactor, float bounceVelocity, int colMask, int frictionMask, int rotation); + GLWidget *glWidget; + DiagramScene *scene; + QTimer *timer; + HenocUniverseI::Whstc myWorldProp; +}; + +#endif diff --git a/FullHenoc/CfrmMundo.cpp b/FullHenoc/CfrmMundo.cpp new file mode 100755 index 0000000..c82ed73 --- /dev/null +++ b/FullHenoc/CfrmMundo.cpp @@ -0,0 +1,44 @@ +#include +#include + + +using namespace HenocUniverseI; + +CfrmMundo::CfrmMundo(Whstc *pW, QWidget *parent, Qt::WindowFlags f):QDialog(parent,f),mW(pW){ + setupUi(this); + connect(btnAceptar, SIGNAL(clicked()), this, SLOT(Aceptar())); + connect(btnCancelar, SIGNAL(clicked()), this, SLOT(Cancelar())); + connect(btnLimpiar, SIGNAL(clicked()), this, SLOT(Limpiar())); + + intfactorDeAnimacion->setValue( mW->fAnim ); + fltdelta->setValue( mW->delta ); + intCorrelacionMaximaDeVelocidad->setValue( mW->cmv ); + fltFactorDeContactoSuperficial->setValue( mW->fcs ); + intGravedad->setValue( mW->gravity ); + fltUmbralDeLinealidad->setValue( mW->lin ); + fltUmbralDeCorreccionDeErrores->setValue( mW->erp ); + intCoaccion->setValue( mW->co ); + +} + +void CfrmMundo::Aceptar(){ + mW->fAnim = intfactorDeAnimacion->value(); + mW->delta = fltdelta->value(); + mW->cmv = intCorrelacionMaximaDeVelocidad->value(); + mW->fcs = fltFactorDeContactoSuperficial->value(); + mW->gravity = intGravedad->value(); + mW->lin = fltUmbralDeLinealidad->value(); + mW->erp = fltUmbralDeCorreccionDeErrores->value(); + mW->co = intCoaccion->value(); + accept(); +} + +void CfrmMundo::Limpiar(){ + +} + +void CfrmMundo::Cancelar(){ + reject(); +} + + diff --git a/FullHenoc/CfrmMundo.h b/FullHenoc/CfrmMundo.h new file mode 100755 index 0000000..8802ad1 --- /dev/null +++ b/FullHenoc/CfrmMundo.h @@ -0,0 +1,23 @@ +#ifndef __CFRMMUNDO_H__ +#define __CFRMMUNDO_H__ + +#include "ui_frmMundo.h" +#include +#include + +using namespace Ui; +using namespace HenocUniverseI; + +class CfrmMundo : public QDialog, frmMundo{ + Q_OBJECT + public: + CfrmMundo(Whstc *pW, QWidget * parent = 0, Qt::WindowFlags f = 0); + public slots: + void Aceptar(); + void Limpiar(); + void Cancelar(); + private: + Whstc *mW; +}; + +#endif diff --git a/FullHenoc/CfrmPeCaLi.cpp b/FullHenoc/CfrmPeCaLi.cpp new file mode 100755 index 0000000..91fb9ef --- /dev/null +++ b/FullHenoc/CfrmPeCaLi.cpp @@ -0,0 +1,86 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + + +CfrmPeCaLi::CfrmPeCaLi(HObject *_obj, QWidget *parent, Qt::WindowFlags f):QDialog(parent,f),mObj(_obj){ + setupUi(this); + + intalto->setEnabled(true); + intlargo->setEnabled(true); + intRadio->setEnabled(true); + intRotacion->setEnabled(true); + connect(btnAceptar, SIGNAL(clicked()), this, SLOT(Aceptar())); + connect(btnCancelar, SIGNAL(clicked()), this, SLOT(Cancelar())); + connect(btnLimpiar, SIGNAL(clicked()), this, SLOT(Limpiar())); + fltdensidad->setValue( mObj->getMass() ); + fltfriccion->setValue( mObj->getFriction() ); + fltfactorDeRebote->setValue( mObj->getBounceFactor() ); + fltvelocidadDeRebote->setValue( mObj->getBounceVelocity() ); + intcolisiones->setValue( mObj->getColMask() ); + intmascaraDeFriccion->setValue( mObj->getFrictionMask() ); + intRotacion->setValue( (int)mObj->getRotation() ); + mObj->lastRotation = mObj->getRotation(); + + switch( mObj->getType() ){ + case 1: + intRadio->setEnabled(false); + intalto->setValue( mObj->height() ); + intlargo->setValue( mObj->width() ); + break; + case 2: + intalto->setEnabled(false); + intlargo->setEnabled(false); + intRotacion->setEnabled(false); + intRadio->setValue( mObj->width() ); + break; + case 3: + intRadio->setEnabled(false); + intalto->setEnabled(false); + intalto->setEnabled(false); + intRotacion->setEnabled(false); + break; + } + +} + +void CfrmPeCaLi::Aceptar(){ + mObj->setMass( fltdensidad->value() ); + mObj->setFriction( fltfriccion->value() ); + mObj->setBounceFactor( fltfactorDeRebote->value() ); + mObj->setBounceVelocity( fltvelocidadDeRebote->value() ); + mObj->setColMask( intcolisiones->value() ); + mObj->setFrictionMask( intmascaraDeFriccion->value() ); + mObj->setRotation( intRotacion->value() ); + + switch( mObj->getType() ){ + case 1: + mObj->setWidth( intlargo->value() ); + mObj->setHeight( intalto->value() ); + break; + case 2: + mObj->setWidth( intRadio->value() ); + mObj->setHeight( intRadio->value() ); + break; + } + + + accept(); +} + + +void CfrmPeCaLi::Limpiar(){ + +} + +void CfrmPeCaLi::Cancelar(){ + reject(); +} + + diff --git a/FullHenoc/CfrmPeCaLi.h b/FullHenoc/CfrmPeCaLi.h new file mode 100755 index 0000000..de37223 --- /dev/null +++ b/FullHenoc/CfrmPeCaLi.h @@ -0,0 +1,26 @@ +#ifndef __CFRMPECALI_H__ +#define __CFRMPECALI_H__ + +#include "ui_frmPeCaLi.h" +#include +#include + +using namespace Ui; + +class HLine; +class HBox; +class HBall; + +class CfrmPeCaLi : public QDialog, frmPeCaLi{ + Q_OBJECT + public: + CfrmPeCaLi(HObject *, QWidget * parent = 0, Qt::WindowFlags f = 0); + public slots: + void Aceptar(); + void Limpiar(); + void Cancelar(); + private: + HObject *mObj; +}; + +#endif diff --git a/FullHenoc/HBall.cpp b/FullHenoc/HBall.cpp new file mode 100755 index 0000000..c0e753b --- /dev/null +++ b/FullHenoc/HBall.cpp @@ -0,0 +1,28 @@ +#include + +#include "HBall.h" + + +HBall::HBall(QGraphicsItem *parent):QGraphicsEllipseItem(parent){ + setFlag(QGraphicsItem::ItemIsMovable, true); +} + + +HBall::HBall( qreal x, qreal y, qreal width, qreal height, QGraphicsItem * parent ):QGraphicsEllipseItem(x,y,width,height,parent){ + setFlag(QGraphicsItem::ItemIsMovable, true); +} + +void HBall::setWidth(int w){ + QRectF fao = rect(); + fao.setWidth(w); + setRect(fao); + obj.setWidth(w); +} + +void HBall::setHeight(int h){ + QRectF fao = rect(); + fao.setHeight(h); + setRect(fao); + obj.setHeight(h); +} + diff --git a/FullHenoc/HBall.h b/FullHenoc/HBall.h new file mode 100755 index 0000000..f24f335 --- /dev/null +++ b/FullHenoc/HBall.h @@ -0,0 +1,31 @@ +#ifndef __HBALL_H__ +#define __HBALL_H__ + +#include +#include +#include "HObject.h" + +class QPixmap; +class QGraphicsItem; +class QGraphicsScene; +class QTextEdit; +class QGraphicsSceneMouseEvent; +class QMenu; +class QGraphicsSceneContextMenuEvent; +class QPainter; +class QStyleOptionGraphicsItem; +class QWidget; +class QPolygonF; + + +class HBall : public QGraphicsEllipseItem{ + public: + HBall(QGraphicsItem *parent = 0); + HBall( qreal x, qreal y, qreal width, qreal height, QGraphicsItem * parent = 0 ); + void setWidth(int); + void setHeight(int); + HObject obj; + private: +}; + +#endif diff --git a/FullHenoc/HBox.cpp b/FullHenoc/HBox.cpp new file mode 100755 index 0000000..28f35fc --- /dev/null +++ b/FullHenoc/HBox.cpp @@ -0,0 +1,28 @@ +#include +#include "HBox.h" + + +HBox::HBox(QGraphicsItem *parent):QGraphicsRectItem(parent){ + setFlag(QGraphicsItem::ItemIsMovable, true); +} + + +HBox::HBox( qreal x, qreal y, qreal width, qreal height, QGraphicsItem * parent ):QGraphicsRectItem(x,y,width,height,parent){ + setFlag(QGraphicsItem::ItemIsMovable, true); +} + +void HBox::setWidth(int w){ + QRectF fao = rect(); + fao.setWidth(w); + setRect(fao); + obj.setWidth(w); +} + +void HBox::setHeight(int h){ + QRectF fao = rect(); + fao.setHeight(h); + setRect(fao); + obj.setHeight(h); +} + + diff --git a/FullHenoc/HBox.h b/FullHenoc/HBox.h new file mode 100755 index 0000000..997ee34 --- /dev/null +++ b/FullHenoc/HBox.h @@ -0,0 +1,31 @@ +#ifndef __HBOX_H__ +#define __HBOX_H__ + +#include +#include +#include "HObject.h" + +class QPixmap; +class QGraphicsItem; +class QGraphicsScene; +class QTextEdit; +class QGraphicsSceneMouseEvent; +class QMenu; +class QGraphicsSceneContextMenuEvent; +class QPainter; +class QStyleOptionGraphicsItem; +class QWidget; +class QPolygonF; + + +class HBox : public QGraphicsRectItem{ + public: + HBox(QGraphicsItem *parent = 0); + HBox( qreal x, qreal y, qreal width, qreal height, QGraphicsItem * parent = 0 ); + void setWidth(int); + void setHeight(int); + HObject obj; + private: +}; + +#endif diff --git a/FullHenoc/HLine.cpp b/FullHenoc/HLine.cpp new file mode 100755 index 0000000..178b94c --- /dev/null +++ b/FullHenoc/HLine.cpp @@ -0,0 +1,23 @@ +#include +#include "HLine.h" + + +HLine::HLine(QGraphicsItem *parent):QGraphicsLineItem(parent){ + //setFlag(QGraphicsItem::ItemIsMovable, true); +} + +HLine::HLine( const QLineF & line, QGraphicsItem * parent):QGraphicsLineItem(line,parent){ + //setFlag(QGraphicsItem::ItemIsMovable, true); +} + +HLine::HLine( qreal x, qreal y, qreal width, qreal height, QGraphicsItem * parent ):QGraphicsLineItem(x,y,width,height,parent){ + //setFlag(QGraphicsItem::ItemIsMovable, true); +} + +void HLine::setLength(int w){ + QLineF fao = line(); + fao.setLength(w); + setLine(fao); + obj.setLength(w); +} + diff --git a/FullHenoc/HLine.h b/FullHenoc/HLine.h new file mode 100755 index 0000000..8d0803a --- /dev/null +++ b/FullHenoc/HLine.h @@ -0,0 +1,31 @@ +#ifndef __HLINE_H__ +#define __HLINE_H__ + +#include +#include +#include "HObject.h" + +class QPixmap; +class QGraphicsItem; +class QGraphicsScene; +class QTextEdit; +class QGraphicsSceneMouseEvent; +class QMenu; +class QGraphicsSceneContextMenuEvent; +class QPainter; +class QStyleOptionGraphicsItem; +class QWidget; +class QPolygonF; + + +class HLine : public QGraphicsLineItem{ + public: + HLine(QGraphicsItem *parent = 0); + HLine( qreal x, qreal y, qreal width, qreal height, QGraphicsItem * parent = 0 ); + HLine( const QLineF & line, QGraphicsItem * parent = 0 ); + void setLength(int); + HObject obj; + private: +}; + +#endif diff --git a/FullHenoc/HObject.cpp b/FullHenoc/HObject.cpp new file mode 100755 index 0000000..173ff98 --- /dev/null +++ b/FullHenoc/HObject.cpp @@ -0,0 +1,44 @@ +#include + +HObject::HObject(){ + mass = 5.0; + friction = 1.0; + bounceFactor = 0.35; + bounceVelocity = 0; + colMask = ~0; + frictionMask = ~0; + rotation = 0; + type = 0; + l = 0; + w = 0; + h = 0; + color = 0.0; + lastRotation = 0.0; +} +; +float HObject::getMass(){ return mass; }; +float HObject::getFriction(){ return friction; }; +float HObject::getBounceFactor(){ return bounceFactor; }; +float HObject::getBounceVelocity(){ return bounceVelocity; }; +int HObject::getColMask(){ return colMask; }; +int HObject::getFrictionMask(){ return frictionMask; }; +float HObject::getRotation(){ return rotation; }; +int HObject::getType(){ return type; }; +int HObject::length(){ return l; }; +int HObject::width(){ return w; }; +int HObject::height(){ return h; }; +float HObject::getColor(){ return color; }; + +void HObject::setMass(float m){ mass = m; }; +void HObject::setFriction(float f){ friction = f; }; +void HObject::setBounceFactor(float b){ bounceFactor = b; }; +void HObject::setBounceVelocity(float b){ bounceVelocity = b; }; +void HObject::setColMask(int c){ colMask = c; }; +void HObject::setFrictionMask(int f){ frictionMask = f; }; +void HObject::setRotation(float r){ rotation = r; }; +void HObject::setType(int t){ type = t; }; +void HObject::setLength(int pl){ l = pl; }; +void HObject::setWidth(int pw){ w = pw; }; +void HObject::setHeight(int ph){ h = ph; }; +void HObject::setColor(float c){ color = c; }; + diff --git a/FullHenoc/HObject.h b/FullHenoc/HObject.h new file mode 100755 index 0000000..746b95c --- /dev/null +++ b/FullHenoc/HObject.h @@ -0,0 +1,50 @@ +#ifndef __HOBJECT_H__ +#define __HOBJECT_H__ + +#include + +class HObject{ + private: + int type; + float mass; + float friction; + float bounceFactor; + float bounceVelocity; + int colMask; + int frictionMask; + float rotation; + int l; + int w; + int h; + float color; + public: + HObject(); + float getMass(); + float getFriction(); + float getBounceFactor(); + float getBounceVelocity(); + int getColMask(); + int getFrictionMask(); + float getRotation(); + int getType(); + int length(); + int width(); + int height(); + float getColor(); + float lastRotation; + + void setMass(float m); + void setFriction(float f); + void setBounceFactor(float b); + void setBounceVelocity(float b); + void setColMask(int c); + void setFrictionMask(int f); + void setRotation(float r); + void setType(int); + void setLength(int); + void setWidth(int); + void setHeight(int); + void setColor(float); +}; + +#endif diff --git a/FullHenoc/Henoc.pro b/FullHenoc/Henoc.pro new file mode 100755 index 0000000..7ec3dab --- /dev/null +++ b/FullHenoc/Henoc.pro @@ -0,0 +1,35 @@ +# +# /usr/include/malloc/ --> directorio necesario para compilar en MacOSX +# en linux esta directamente en /usr/include +# + +INCLUDEPATH += ../HenocUniverse/ /usr/include/malloc/ +LIBS += -L. -L../HenocUniverse/ -lHenocUniverse +CONFIG += warn_off static release +FORMS += frmHenoc.ui \ + frmPeCaLi.ui \ + frmMundo.ui +SOURCES += main.cpp \ + diagramscene.cpp\ + HObject.cpp\ + HLine.cpp\ + HBox.cpp\ + HBall.cpp\ + glwidget.cpp \ + CfrmPeCaLi.cpp \ + CfrmMundo.cpp \ + CfrmHenoc.cpp +HEADERS += CfrmHenoc.h\ + HObject.h\ + HBox.h\ + HLine.h\ + HBall.h\ + glwidget.h \ + CfrmPeCaLi.h \ + CfrmMundo.h \ + diagramscene.h +RESOURCES += diagramscene.qrc + +DESTDIR += ../ + +QT += opengl xml diff --git a/FullHenoc/diagramscene.cpp b/FullHenoc/diagramscene.cpp new file mode 100755 index 0000000..ae97d71 --- /dev/null +++ b/FullHenoc/diagramscene.cpp @@ -0,0 +1,220 @@ +#include + +#include "diagramscene.h" +#include "HObject.h" +#include "HBall.h" +#include "HBox.h" +#include "HLine.h" + +#include + +#include + +using namespace std; + +DiagramScene::DiagramScene(QMenu *itemMenu, QObject *parent): QGraphicsScene(parent){ + myItemMenu = itemMenu; + myMode = MoveItem; + myItemType = 0; + itemx = 0; + myItemColor = Qt::white; + myLineColor = Qt::black; +} + +void DiagramScene::setLineColor(const QColor &color) +{ +} + +QColor fromThetaIncreasedColor(float theta, float factor){ + float r,g,b; + r = g = b = 0; + if (theta < 0) + theta = 360 + theta; + if (theta >= 360) + theta -= 360; + + if (theta < 120){ + g = theta / 120; + r = 1 - g; + b = 0; + } + else if (theta < 240){ + b = (theta - 120) / 120; + g = 1 - b; + r = 0; + } + else{ + r = (theta - 240) / 120; + b = 1 - r; + g = 0; + } + r += factor; + g += factor; + b += factor; + if (r > 1) + r = 1; + if (g > 1) + g = 1; + if (b > 1) + b = 1; + return(QColor(r*255,g*255,b*255)); +} + +QColor fromThetaColor(float theta){ + float r,g,b; + r = g = b = 0; + if (theta < 0) + theta = 360 + theta; + if (theta >= 360) + theta -= 360; + + if (theta < 120){ + g = theta / 120; + r = 1 - g; + b = 0; + } + else if (theta < 240){ + b = (theta - 120) / 120; + g = 1 - b; + r = 0; + } + else{ + r = (theta - 240) / 120; + b = 1 - r; + g = 0; + } + return(QColor(r*255,g*255,b*255)); +} + +void DiagramScene::setItemColor(const QColor &color) +{ +/* myItemColor = color; + if (isItemChange(DiagramItem::Type)) { + DiagramItem *item = + qgraphicsitem_cast(selectedItems().first()); + item->setBrush(myItemColor); + } +*/ +} + +void DiagramScene::setMode(Mode mode) +{ + myMode = mode; +} + +void DiagramScene::setItemType(int type) +{ + myItemType = type; +} + + +void DiagramScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent){ + if (mouseEvent->button() != Qt::LeftButton) + return; + + QGraphicsItem *item; + switch (myMode){ + case InsertItem: + if(itemx != 0) break; + if(0 == myItemType ) break; + if(1 == myItemType ){ + item = new HBox(10,10,20,20); + ((HBox *)item)->obj.setType(1); + float tColor = (60.0f + (rand() % 90) - 45); + ((HBox *)item)->obj.setColor( tColor ); + ((QGraphicsRectItem*)(item))->setPen( QPen( fromThetaColor(tColor) ) ); + ((QGraphicsRectItem*)(item))->setBrush(fromThetaIncreasedColor(tColor, 0.5)); + ((QGraphicsRectItem*)(item))->resetMatrix(); + } + if(2 == myItemType ){ + item = new HBall(10,10,20,20); + ((HBall *)item)->obj.setType(2); + float tColor = (60.0f + (rand() % 90) - 45); + ((HBall *)item)->obj.setColor( tColor ); + ((QGraphicsEllipseItem*)(item))->setPen( QPen( fromThetaColor(tColor) ) ); + ((QGraphicsEllipseItem*)(item))->setBrush(fromThetaIncreasedColor(tColor, 0.5)); + //item = new QGraphicsEllipseItem(10,10,20,20); + } + if(3 == myItemType ){ + item = new HLine(QLineF(mouseEvent->scenePos(), mouseEvent->scenePos())); + ((HLine *)item)->obj.setType(3); + item->setFlag(QGraphicsItem::ItemIsSelectable, true); + float tColor = (60.0f + (rand() % 90) - 45); + ((HLine *)item)->obj.setColor( tColor ); + ((QGraphicsLineItem*)item)->setPen(QPen(fromThetaColor(tColor), 2)); + addItem(item); + itemx = item; + break; + } + //item->setBrush(myItemColor); + //item->setFlag(QGraphicsItem::ItemIsMovable, true); + item->setFlag(QGraphicsItem::ItemIsSelectable, true); + addItem(item); + //item->setPos( mouseEvent->scenePos() ); + item->setPos(mouseEvent->scenePos() -= QPointF(25,25)); + itemx = item; + //emit itemInserted(item); + break; + default: + ; + } + QGraphicsScene::mousePressEvent(mouseEvent); +} + +void DiagramScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent){ + if(myMode == InsertItem && itemx != 0){ + int xa = views().at(0)->mapFromScene( itemx->mapToScene( itemx->boundingRect() )).boundingRect().x(); + int ya = views().at(0)->mapFromScene( itemx->mapToScene( itemx->boundingRect() )).boundingRect().y(); + int wa = views().at(0)->mapFromScene( itemx->mapToScene( itemx->boundingRect() )).boundingRect().width(); + int ha = views().at(0)->mapFromScene( itemx->mapToScene( itemx->boundingRect() )).boundingRect().height(); + QRectF fao; + if(1 == myItemType ){ + fao = ((QGraphicsRectItem*)itemx)->rect(); + fao.setHeight( fao.height() + (mouseEvent->scenePos().y() - mouseEvent->lastScenePos().y()) ); + fao.setWidth( fao.width() + (mouseEvent->scenePos().x() - mouseEvent->lastScenePos().x()) ); + } + if(2 == myItemType ){ + fao = ((QGraphicsEllipseItem*)itemx)->rect(); + qreal fx = (mouseEvent->scenePos().x() - mouseEvent->lastScenePos().x()); + qreal fy = (mouseEvent->scenePos().y() - mouseEvent->lastScenePos().y()); + fao.setHeight( fao.height() + ((fx+fy)/2) ); + fao.setWidth( fao.width() + ((fx+fy)/2) ); + } + if(1 == myItemType ) + ((QGraphicsRectItem*)itemx)->setRect(fao); + if(2 == myItemType ) + ((QGraphicsEllipseItem*)itemx)->setRect(fao); + + if (3 == myItemType) { + QLineF newLine(((QGraphicsLineItem*)itemx)->line().p1(), mouseEvent->scenePos()); + ((QGraphicsLineItem*)itemx)->setLine(newLine); + } + } + else if (myMode == MoveItem) { + QGraphicsScene::mouseMoveEvent(mouseEvent); + } +} + +void DiagramScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent){ + if(myMode == InsertItem && itemx != 0){ + if(3 != myItemType){ + ((HBall *)itemx)->obj.setWidth(views().at(0)->mapFromScene( itemx->mapToScene( itemx->boundingRect() )).boundingRect().width()); + ((HBall *)itemx)->obj.setHeight(views().at(0)->mapFromScene( itemx->mapToScene( itemx->boundingRect() )).boundingRect().height()); + ((HBall *)itemx)->obj.setMass( ( (float)(itemx->boundingRect().width()+itemx->boundingRect().height())/40.0 ) ); + } + myItemType = 0; + emit itemInserted(itemx); + //delete itemx; + itemx=0; + } + + QGraphicsScene::mouseReleaseEvent(mouseEvent); +} + +bool DiagramScene::isItemChange(int type){ + foreach (QGraphicsItem *item, selectedItems()) { + if (item->type() == type) + return true; + } + return false; +} diff --git a/FullHenoc/diagramscene.h b/FullHenoc/diagramscene.h new file mode 100755 index 0000000..4a317ab --- /dev/null +++ b/FullHenoc/diagramscene.h @@ -0,0 +1,58 @@ +#ifndef DIAGRAMSCENE_H +#define DIAGRAMSCENE_H + +#include + +class QGraphicsSceneMouseEvent; +class QMenu; +class QPointF; +class QGraphicsLineItem; +class QFont; +class QGraphicsTextItem; +class QColor; + +class DiagramScene : public QGraphicsScene +{ + Q_OBJECT + +public: + enum Mode { InsertItem, InsertLine, InsertText, MoveItem }; + + DiagramScene(QMenu *itemMenu, QObject *parent = 0); + QColor itemColor() const + { return myItemColor; } + QColor lineColor() const + { return myLineColor; } + void setLineColor(const QColor &color); + void setItemColor(const QColor &color); + +public slots: + void setMode(Mode mode); + void setItemType(int); + +signals: + void itemInserted(QGraphicsItem *item); +// void itemSelected(QGraphicsItem *item); + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent); + void mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent); + +private: + bool isItemChange(int type); + int myItemType; + QMenu *myItemMenu; + QGraphicsItem *itemx; + Mode myMode; + bool leftButtonDown; + QPointF startPoint; + //QGraphicsLineItem *line; + QColor myItemColor; + QColor myLineColor; +}; + +QColor fromThetaIncreasedColor(float theta, float factor); +QColor fromThetaColor(float theta); + +#endif diff --git a/FullHenoc/diagramscene.pro b/FullHenoc/diagramscene.pro new file mode 100755 index 0000000..6aa89af --- /dev/null +++ b/FullHenoc/diagramscene.pro @@ -0,0 +1,20 @@ +HEADERS = mainwindow.h \ + diagramitem.h \ + diagramscene.h \ + arrow.h \ + diagramtextitem.h +SOURCES = mainwindow.cpp \ + diagramitem.cpp \ + main.cpp \ + arrow.cpp \ + diagramtextitem.cpp \ + diagramscene.cpp +RESOURCES = diagramscene.qrc + +CONFIG += warn_off static release + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/graphicsview/diagramscene +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS diagramscene.pro images +sources.path = $$[QT_INSTALL_EXAMPLES]/graphicsview/diagramscene +INSTALLS += target sources diff --git a/FullHenoc/diagramscene.qrc b/FullHenoc/diagramscene.qrc new file mode 100755 index 0000000..c6b2176 --- /dev/null +++ b/FullHenoc/diagramscene.qrc @@ -0,0 +1,47 @@ + + + images/background1.png + images/background2.png + images/background3.png + images/background4.png + images/bold.png + images/bringtofront.png + images/delete.png + images/floodfill.png + images/italic.png + images/linecolor.png + images/linepointer.png + images/pointer.png + images/sendtoback.png + images/textpointer.png + images/underline.png + + + images/Botones/Config.png + images/Botones/Enoch21.JPG + images/Botones/Folder-Secure.png + images/Botones/My-Documents.png + images/Botones/SSave.png + images/Botones/Symbol-Stop.png + images/Botones/catapulta.png + images/Botones/catapulta2.jpg + images/Botones/circle.png + images/Botones/Config.png + images/Botones/dr-boton_linea.jpg + images/Botones/editredo.png + images/Botones/enmarca.png + images/Botones/enmarca2.png + images/Botones/enmarcax.png + images/Botones/fileclose.png + images/Botones/fileopen.png + images/Botones/folder.png + images/Botones/ok.png + images/Botones/rectangle.png + images/Botones/remove.png + images/Botones/rotateright.png + images/Botones/save2.png + images/Botones/trash.png + images/Botones/update.png + images/Botones/wmp.png + + diff --git a/FullHenoc/frmCatapulta.ui b/FullHenoc/frmCatapulta.ui new file mode 100755 index 0000000..7f21e8d --- /dev/null +++ b/FullHenoc/frmCatapulta.ui @@ -0,0 +1,201 @@ + + frmCatapulta + + + + 0 + 0 + 296 + 292 + + + + PROPIEDADESD DE LA CATAPULTA + + + + + + + + Grosor + + + + + + + Qt::Horizontal + + + + 141 + 20 + + + + + + + + + + + Colisiones + + + + + + + Qt::Horizontal + + + + 141 + 20 + + + + + + + + + + + Mascara De Friccion + + + + + + + Qt::Horizontal + + + + 101 + 20 + + + + + + + + + + + Largo + + + + + + + Qt::Horizontal + + + + 191 + 20 + + + + + + + + + + + Alto + + + + + + + Qt::Horizontal + + + + 161 + 20 + + + + + + + + + + + Orientacion + + + + + + + Oeste + + + + + + + Este + + + + + + + Bisagra + + + + + + + Qt::Horizontal + + + + + + + + + + + Limpiar + + + + + + + Cancelar + + + + + + + Aceptar + + + + + + + + + + + + diff --git a/FullHenoc/frmHenoc.ui b/FullHenoc/frmHenoc.ui new file mode 100755 index 0000000..07ffc8e --- /dev/null +++ b/FullHenoc/frmHenoc.ui @@ -0,0 +1,292 @@ + + frmHenoc + + + + 0 + 0 + 800 + 590 + + + + + 800 + 590 + + + + + 800 + 590 + + + + Henoc + + + + + 9 + + + 6 + + + + + + 91 + 0 + + + + + 91 + 16777215 + + + + Menu + + + + 9 + + + 6 + + + + + CAJA + + + + + + :/Botones/images/Botones/rectangle.png + + + + 35 + 35 + + + + + + + + BOLA + + + + + + :/Botones/images/Botones/circle.png + + + + 35 + 35 + + + + + + + + LINEA + + + + + + :/Botones/images/Botones/dr-boton_linea.jpg + + + + 35 + 35 + + + + + + + + ENMARCA + + + + + + :/Botones/images/Botones/enmarcax.png + + + + 35 + 35 + + + + + + + + BORRAR OBJETO + + + + + + :/Botones/images/Botones/trash.png + + + + 35 + 35 + + + + + + + + PROPIEDADES + + + + + + :/Botones/images/Botones/Config.png + + + + 35 + 35 + + + + + + + + GUARDAR + + + + + + :/Botones/images/Botones/SSave.png + + + + 35 + 35 + + + + + + + + ABRIR + + + + + + :/Botones/images/Botones/Folder-Secure.png + + + + 35 + 35 + + + + + + + + Qt::Horizontal + + + + + + + INICIAR + + + Play + + + :/Botones/images/Botones/wmp.png + + + + + + + PARAR + + + Stop + + + :/Botones/images/Botones/Symbol-Stop.png + + + + 20 + 20 + + + + + + + + + + + 0 + + + + + 9 + + + 6 + + + + + + + + + + + + + + + 0 + 0 + 800 + 29 + + + + + + + + + + diff --git a/FullHenoc/frmMundo.ui b/FullHenoc/frmMundo.ui new file mode 100755 index 0000000..bd4fd14 --- /dev/null +++ b/FullHenoc/frmMundo.ui @@ -0,0 +1,212 @@ + + frmMundo + + + + 0 + 0 + 289 + 293 + + + + PROPIEDADES DEL MUNDO + + + + + + + + Factor de Animacion + + + + + + + Qt::Horizontal + + + + 81 + 20 + + + + + + + + + + + Delta + + + + + + + Qt::Horizontal + + + + 191 + 20 + + + + + + + + 1.000000000000000 + + + 0.050000000000000 + + + + + + + Correlacion Maxima de Velocidad + + + + + + + + + + Factor de Contacto Superficial + + + + + + + + + + Gravedad + + + + + + + Qt::Horizontal + + + + 161 + 20 + + + + + + + + 0.100000000000000 + + + + + + + Umbral de Linealidad + + + + + + + Qt::Horizontal + + + + 81 + 20 + + + + + + + + + + + Umbral de Correccion de Errores + + + + + + + 1.000000000000000 + + + 0.050000000000000 + + + + + + + Coaccion + + + + + + + Qt::Horizontal + + + + 111 + 20 + + + + + + + + + + + + + + + Aceptar + + + + + + + Limpiar + + + + + + + Cancelar + + + + + + + + + + diff --git a/FullHenoc/frmPeCaLi.ui b/FullHenoc/frmPeCaLi.ui new file mode 100755 index 0000000..5fe2edd --- /dev/null +++ b/FullHenoc/frmPeCaLi.ui @@ -0,0 +1,199 @@ + + frmPeCaLi + + + + 0 + 0 + 372 + 251 + + + + PROPIEDADES PELOTA, CAJA Y LINEA + + + + + + + + Masa + + + + + + + + + + Friccion + + + + + + + + + + Factor de Rebote + + + + + + + 10.000000000000000 + + + 0.050000000000000 + + + + + + + Velocidad de Rebote + + + + + + + + + + Colisiones + + + + + + + -99 + + + + + + + Mascara de Friccion + + + + + + + -99 + + + + + + + Largo + + + + + + + 800 + + + 1 + + + + + + + Alto + + + + + + + 600 + + + + + + + Rotacion + + + + + + + -360 + + + 360 + + + + + + + Radio + + + + + + + Qt::Horizontal + + + + 21 + 20 + + + + + + + + 700 + + + + + + + + + + + Aceptar + + + + + + + Limpiar + + + + + + + Cancelar + + + + + + + + + + diff --git a/FullHenoc/glwidget.cpp b/FullHenoc/glwidget.cpp new file mode 100755 index 0000000..b646d3b --- /dev/null +++ b/FullHenoc/glwidget.cpp @@ -0,0 +1,219 @@ +#include +#include + +#include + +#include "glwidget.h" +#include "henocUniverseI.cpp" + +#include + +using namespace std; +using namespace HenocUniverseI; + +const int MaxObjects = 40; + +void AddFallingObject(Space &space, Object *object){ + if (space.size() >= MaxObjects) + space.pop(); + + Color pastel(60.0f + (rand() % 90) - 45); + object->Property().outlineColor = pastel; + pastel.IncreaseBrightness(0.5); + object->Property().fillColor = pastel; + object->Property().outlineThickness = 1; + object->GetFlatlandObject()->SetMass(5.1); + space.push_back(object); +} + +GLWidget::GLWidget(QWidget *parent) + : QGLWidget(parent){ + //app(width(), height() ); +} + +GLWidget::~GLWidget(){ + exit(0); +} + +QSize GLWidget::minimumSizeHint() const{ + return QSize(600, 500); +} + +QSize GLWidget::sizeHint() const{ + return QSize(800, 600); +} + + +void GLWidget::initializeGL(){ + app.InitGl(); + app.InitOde(); + + // Insert some objects into the space. + Space &space = app.GetSpace(); + //space << new Demo::Line(vec2(0, 0), vec2(655, 517)); + //space << new Demo::Line(vec2(800, 0), vec2(800, 600)); + //space << new Demo::Line(vec2(0, 0), vec2(0, 600)); + /*{ + Wheel *wheel = new Wheel(vec2(390, 550), 50); + wheel->GetFlatlandObject()->SetMass(200); + //wheel->GetFlatlandObject()->SetMass(11000); + wheel->Property().outlineThickness = 1; + wheel->Insert(space); + }*/ + + // Catapult + /*{ + vec2 center(500, 200); + float width = 200; + float height = 5; + vec2 hinge(center.x - width / 4, center.y); + Catapult *c = new Catapult(center, width, height, hinge); + c->Rotate(-22); + c->Insert(space); + c->InsertSouthEastAnchor(space); + + center += vec2(90, -50); + Block *b = new Block(center, 10, 10); + b->GetFlatlandObject()->SetMass(5.5); + b->GetFlatlandObject()->Property().frictionMask = 1;//dInfinity; + b->Property().fillColor = Color::PaleYellow; + b->Property().outlineColor = Color::PaleYellow; + b->Insert(space); + + center.x -= 14; + b = new Block(center, 10, 10); + b->GetFlatlandObject()->SetMass(0.1); + b->GetFlatlandObject()->Property().frictionMask = 1;//dInfinity; + b->GetFlatlandObject()->Property().friction = 1;//dInfinity; + b->Property().fillColor = Color::PaleYellow; + b->Property().outlineColor = Color::PaleYellow; + b->Insert(space); + }*/ + space.mark(); +} + +void GLWidget::paintGL(){ + app.Draw(); +} + +void GLWidget::resizeGL(int width, int height){ + app.Resize(width, height); +} + + +void GLWidget::animate(){ + const float fps = 40.0; + static int stepCount = 0; + // Simulation step size; proportional to speed and inversely proportional to precision. + const float delta = 0.25; + +/* unsigned int currentTime = osGetMilliseconds(); + unsigned int previousSampleTime = currentTime; + unsigned int previousDrawTime = currentTime; + const unsigned int sampleDelay = 5000; + const unsigned int drawDelay = (unsigned int) (1000.0 / fps); + + // Count the simulation "steps" (iterations) since the last performance sample. + unsigned int stepCount = 0; +*/ + + + Space &space = app.GetSpace(); + app.Draw(); + app.Step(delta); + ++stepCount; + // + // +/* if (space.size() < MaxObjects) + { + if (!(stepCount % 100)) + AddFallingObject(space, new Ball(vec2(400, 0), 15)); + if (!((stepCount + 50) % 100)) + AddFallingObject(space, new Block(vec2(400, 0), 20, 20)); + } +*/ + //AddFallingObject(space, new Ball(vec2(400, 0), 25)); + updateGL(); + +} + +void GLWidget::addBox(int x, int y, int w, int h, float mass, float friction, float bounceFactor, float bounceVelocity, int colMask, int frictionMask, int rotation, float pColor ){ + Space &space = app.GetSpace(); + Object *object = new Block(vec2(x,y), w, h); + Color pastel(pColor); + object->Property().outlineColor = pastel; + pastel.IncreaseBrightness(0.5); + object->Property().fillColor = pastel; + object->Property().outlineThickness = 1; + object->GetFlatlandObject()->SetMass(mass); + + object->GetFlatlandObject()->Property().friction = ( friction > 99 ) ? dInfinity : friction ; + object->GetFlatlandObject()->Property().bounceFactor = bounceFactor; + object->GetFlatlandObject()->Property().bounceVelocity = bounceVelocity; + object->GetFlatlandObject()->Property().bounceFactor = bounceFactor; + object->GetFlatlandObject()->Property().collisionMask = (!colMask) ? ~0 : colMask; + object->GetFlatlandObject()->Property().frictionMask = (!frictionMask) ? ~0 : frictionMask; + + if(rotation){ + object->GetFlatlandObject()->Rotate(rotation); + } + + space.push_back(object); +} + +void GLWidget::addBall(int x, int y, int r, float mass, float friction, float bounceFactor, float bounceVelocity, int colMask, int frictionMask, int rotation, float pColor ){ + + //cout << "x=" << x << endl; + //cout << "y=" << y << endl; + //cout << "r=" << y << endl; + //cout << "mass=" << mass << endl; + + Space &space = app.GetSpace(); + Object *object = new Ball(vec2(x,y), r); + Color pastel(pColor); + //Color pastel(60.0f + (rand() % 90) - 45); + object->Property().outlineColor = pastel; + pastel.IncreaseBrightness(0.5); + object->Property().fillColor = pastel; + object->Property().outlineThickness = 1; + object->GetFlatlandObject()->SetMass(mass); + + object->GetFlatlandObject()->Property().friction = ( friction > 99 ) ? dInfinity : friction ; + object->GetFlatlandObject()->Property().bounceFactor = bounceFactor; + object->GetFlatlandObject()->Property().bounceVelocity = bounceVelocity; + object->GetFlatlandObject()->Property().bounceFactor = bounceFactor; + object->GetFlatlandObject()->Property().collisionMask = (!colMask) ? ~0 : colMask; + object->GetFlatlandObject()->Property().frictionMask = (!frictionMask) ? ~0 : frictionMask; + +// if(rotation){ +// object->GetFlatlandObject()->Rotate(rotation); +// } + + space.push_back(object); +} + +void GLWidget::addLine(int x, int y, int w, int h, float friction, int colMask, int frictionMask, float pColor){ + Space &space = app.GetSpace(); + + Line *l = new Line(vec2(x, y), vec2(w, h)); + //l->GetFlatlandObject()->Property().friction = friction; + //l->GetFlatlandObject()->Property().frictionMask = (!frictionMask) ? ~0 : frictionMask; + //l->GetFlatlandObject()->Property().collisionMask = (!colMask) ? ~0 : colMask; + Color pastel(pColor); + l->Property().outlineColor = pastel; + + l->Insert(space); + +// space << new HenocUniverseI::Line(vec2(x, y), vec2(w, h)); +} + +void GLWidget::delSpace(){ + Space &space = app.GetSpace(); + space.destruye(); +} + +void GLWidget::setWorldParams(const Whstc &pw){ + app.setOdeW(pw); +} + + diff --git a/FullHenoc/glwidget.h b/FullHenoc/glwidget.h new file mode 100755 index 0000000..0ea1be1 --- /dev/null +++ b/FullHenoc/glwidget.h @@ -0,0 +1,37 @@ +#ifndef GLWIDGET_H +#define GLWIDGET_H + +#include +#include "henocUniverseI.h" + +using namespace HenocUniverseI; + +class GLWidget : public QGLWidget{ + Q_OBJECT + +public: + GLWidget(QWidget *parent = 0); + ~GLWidget(); + + QSize minimumSizeHint() const; + QSize sizeHint() const; + void addBall(int, int, int, float, float, float, float, int, int, int, float); + void addBox(int, int, int, int, float, float, float, float, int, int, int, float); + void addLine(int, int, int, int, float, int, int, float); + void setWorldParams(const Whstc &); + void delSpace(); + +protected: + void initializeGL(); + void paintGL(); + void resizeGL(int width, int height); + +public slots: + void animate(); + +private: + HenocWrapper app; + +}; + +#endif diff --git a/FullHenoc/henocUniverseI.cpp b/FullHenoc/henocUniverseI.cpp new file mode 100755 index 0000000..df0a550 --- /dev/null +++ b/FullHenoc/henocUniverseI.cpp @@ -0,0 +1,889 @@ +/* + * TT102 HENOC + * 2008 @ ESCOM-IPN + * Implementacion de HenocUniverse + */ + + +#include +#include +#include +#include +#include "henocUniverseI.h" + +using HenocUniverse::vec2; +using namespace HenocUniverseI; + +Color Color::Transparent(0,0,0,0); +Color Color::Black(0,0,0,1); +Color Color::White(1,1,1,1); +Color Color::Red(1,0,0,1); +Color Color::Green(0,1,0,1); +Color Color::Blue(0,0,1,1); +Color Color::PaleRed(1,0.6f,0.6f,1); +Color Color::PaleYellow(0.9f,0.8f,0.5f,1); +Color Color::PaleGreen(0.75f,0.85f,0.75f,1); + +const float Point::Size = 10; + +void HenocUniverseI::glColor(const Color &color) { glColor4fv((float*) &color); } +void HenocUniverseI::glVertex(const vec2 &v) { glVertex2fv((float*) &v); } + +ObjectProperties Object::defaults ={ + 2, // outlineThickness + {1, 1, 1, 0.5}, // fillColor + {1, 1, 1, 0}, // altFillColor + {1, 1, 1, 1}, // outlineColor + false // offset +}; + +std::stack Object::defaultStack; + +#define AppInstance HenocWrapper::GetSingleton() +#define World AppInstance.GetWorld() + +void HenocUniverseI::Draw(const HenocUniverse::Geometry &g, const ObjectProperties &appearance){ + switch (g.GetShape()){ + case HenocUniverse::Shape::Circle: DrawCircle(g, appearance); return; + case HenocUniverse::Shape::Quad: DrawQuad(g, appearance); return; + } +} + +void HenocUniverseI::DrawCircle(const HenocUniverse::Geometry &g, const ObjectProperties &appearance){ + assert(g.GetShape() == HenocUniverse::Shape::Circle); + const HenocUniverse::Circle &circle = static_cast(g); + + const float width = appearance.outlineThickness; + const Color &fill = appearance.fillColor; + const Color &outline = appearance.outlineColor; + const float offset = appearance.offset ? width / 2 : 0; + + float radius = circle.Radius() - offset; + const int slices = (int) (radius <= 10 ? 20 : radius); + const float delta = M_PI * 2 / slices; + + if (fill.a){ + glColor(fill); + glBegin(GL_POLYGON); + for (float theta = 0; theta < M_PI * 2; theta += delta) + glVertex(circle.Center() + vec2(sinf(theta), cosf(theta)) * radius); + glEnd(); + } + + if (!width || !outline.a) return; + + if (width > 2){ + float inner = radius - width / 2; + float outer = radius + width / 2; + + glColor(outline); + glBegin(GL_QUAD_STRIP); + for (float theta = 0; theta < M_PI * 2 + delta; theta += delta){ + glVertex(circle.Center() + vec2(sinf(theta), cosf(theta)) * inner); + glVertex(circle.Center() + vec2(sinf(theta), cosf(theta)) * outer); + } + glEnd(); + + glLineWidth(1); + glBegin(GL_LINE_LOOP); + for (float theta = 0; theta < M_PI * 2; theta += delta) + glVertex(circle.Center() + vec2(sinf(theta), cosf(theta)) * inner); + glEnd(); + glBegin(GL_LINE_LOOP); + for (float theta = 0; theta < M_PI * 2; theta += delta) + glVertex(circle.Center() + vec2(sinf(theta), cosf(theta)) * outer); + glEnd(); + + return; + } + + glLineWidth(width); + glColor(outline); + glBegin(GL_LINE_LOOP); + for (float theta = 0; theta < M_PI * 2; theta += delta) + glVertex(circle.Center() + vec2(sinf(theta), cosf(theta)) * radius); + glEnd(); +} + +void HenocUniverseI::DrawQuad(const HenocUniverse::Geometry &g, const ObjectProperties &appearance){ + assert(g.GetShape() == HenocUniverse::Shape::Quad); + const HenocUniverse::Quad &quad = static_cast(g); + + const float width = appearance.outlineThickness; + const Color &fill = appearance.fillColor; + const Color &outline = appearance.outlineColor; + const float offset = appearance.offset ? width / 2 : 0; + + vec2 eaxis[2] ={ + quad.Axis(0) * (quad.Extent(0) - offset), + quad.Axis(1) * (quad.Extent(1) - offset) + }; + + vec2 corners[4]; + const vec2 ¢er = quad.Center(); + corners[0] = center - eaxis[0] - eaxis[1]; + corners[1] = center + eaxis[0] - eaxis[1]; + corners[2] = center + eaxis[0] + eaxis[1]; + corners[3] = center - eaxis[0] + eaxis[1]; + + if (fill.a){ + glColor(fill); + glBegin(GL_QUADS); + glVertex(corners[0]); + glVertex(corners[1]); + glVertex(corners[2]); + glVertex(corners[3]); + glEnd(); + } + + if (!width || !outline.a) return; + + glLineWidth(width); + glColor(outline); + glBegin(GL_LINE_LOOP); + glVertex(corners[0]); + glVertex(corners[1]); + glVertex(corners[2]); + glVertex(corners[3]); + glEnd(); + + glPointSize(width); + glBegin(GL_POINTS); + glVertex(corners[0]); + glVertex(corners[1]); + glVertex(corners[2]); + glVertex(corners[3]); + glEnd(); + glPointSize(Point::Size); +} + +Space::~Space(){ + for (m = list.begin(); m != list.end(); ++m) + delete *m; +} + +Wall::Wall(float left, float top, float right, float bottom) : +object(new HenocUniverse::Block(left, top, right, bottom)){ +} + +Wall::Wall(vec2 center, float width, float height) : +object(new HenocUniverse::Block(center, width, height)){ +} + +Block::Block(vec2 center, float width, float height) : +object(new HenocUniverse::Block(center, width, height), World.BodyCreate()){ +} + +Beam::Beam(vec2 start, vec2 end, float thickness) : Composite((start + end) / 2){ + vec2 center = (start + end) / 2; + float length = (end - start).length(); + vec2 axis(length / 2, 0); + + HenocUniverse::Block *spine = new HenocUniverse::Block(center, length, thickness); + HenocUniverse::Circle *end1 = new HenocUniverse::Circle(center + axis, thickness / 2); + HenocUniverse::Circle *end2 = new HenocUniverse::Circle(center - axis, thickness / 2); + + Add(spine); + Add(end1); + Add(end2); + + theta = 180 / M_PI * atan2f(end.y - start.y, end.x - start.x); +} + +void Beam::Insert(Space &space){ + Composite::Insert(space); + GetFlatlandObject()->Rotate(theta); +} + +void Beam::InsertFront(Space &space){ + Composite::InsertFront(space); + GetFlatlandObject()->Rotate(theta); +} + +Line::Line(vec2 a, vec2 b) : object(new HenocUniverse::Line(a, b)) {} +RopeSegment::RopeSegment(vec2 a, vec2 b) : object(new HenocUniverse::Line(a, b), World.BodyCreate()){ +} + +Terrain::Terrain(vec2 start, float bottom, bool borders, bool borderVisibility) : bottom(bottom), +borders(borders), borderVisibility(borderVisibility), object(new HenocUniverse::Terrain(start)){ +} + +Ball::Ball(HenocUniverse::vec2 center, float radius) : +object(new HenocUniverse::Circle(center, radius), World.BodyCreate()){ + object.Property().friction = 0; +} + +Point::Point(HenocUniverse::vec2 center) : +object(new HenocUniverse::Circle(center, Size / 2), World.BodyCreate()){ + Property().fillColor = Color(1,0,0,1); +} + +Wheel::Wheel(HenocUniverse::vec2 center, float radius) : +object(new HenocUniverse::Circle(center, radius), World.BodyCreate()){ + texture = 0; +} + +Composite::Composite(vec2 centroid) : +object(new HenocUniverse::Composite(centroid), World.BodyCreate()){ +} + +void Terrain::Add(const HenocUniverse::vec2 &v){ + object.GetGeometry().push_back(v); +} + +void Composite::Add(HenocUniverse::Geometry *geometry){ + object.GetGeometry().push_back(geometry); +} + +void Wall::Draw() const{ + DrawQuad(object.GetGeometry(), Property()); +} + +void Ball::Draw() const{ + DrawCircle(object.GetGeometry(), Property()); +} + +void Point::Draw() const{ + glColor(Property().fillColor); + glBegin(GL_POINTS); + glVertex(object.GetGeometry().Center()); + glEnd(); +} + +void Block::Draw() const{ + DrawQuad(object.GetGeometry(), Property()); +} + +void Wheel::Draw() const{ + if (texture){ + float radius; + glGetTexLevelParameterfv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &radius); + radius /= 2; + + vec2 axis0 = object.GetGeometry().Axis(0); + vec2 axis1 = object.GetGeometry().Axis(1); + vec2 eaxis[2] = { axis0 * radius, axis1 * radius }; + + vec2 corners[4]; + const vec2 ¢er = object.GetGeometry().Center(); + corners[0] = center - eaxis[0] - eaxis[1]; + corners[1] = center + eaxis[0] - eaxis[1]; + corners[2] = center + eaxis[0] + eaxis[1]; + corners[3] = center - eaxis[0] + eaxis[1]; + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, texture); + glColor(Color::White); + glPushMatrix(); + glBegin(GL_QUADS); + glTexCoord2f(0, 0); + glVertex(corners[0]); + glTexCoord2f(1, 0); + glVertex(corners[1]); + glTexCoord2f(1, 1); + glVertex(corners[2]); + glTexCoord2f(0, 1); + glVertex(corners[3]); + glEnd(); + glPopMatrix(); + glDisable(GL_TEXTURE_2D); + return; + } + + const float radius = object.GetGeometry().Radius(); + const vec2 axis = object.GetGeometry().Axis(); + const vec2 center = object.GetGeometry().Center(); + const int slices = (int) radius; + const float delta = M_PI / slices; + + glColor(Property().fillColor); + glBegin(GL_POLYGON); + for (float theta = 0; theta < M_PI * 2 + delta / 2; theta += delta){ + vec2 spine(sinf(theta), cosf(theta)); + glVertex(center + spine.rotate(axis.flip()) * radius); + } + glEnd(); + + glColor(Property().altFillColor); + glBegin(GL_POLYGON); + for (float theta = M_PI; theta < M_PI * 2 + delta / 2; theta += delta){ + vec2 spine(sinf(theta), cosf(theta)); + glVertex(center + spine.rotate(axis.flip()) * radius); + } + glEnd(); + + if (!Property().outlineThickness) + return; + + glLineWidth(Property().outlineThickness); + glColor(Property().outlineColor); + glBegin(GL_LINE_STRIP); + for (float theta = 0; theta < M_PI * 2 + delta / 2; theta += delta) + glVertex(center + vec2(sinf(theta), cosf(theta)) * radius); + glEnd(); + glBegin(GL_LINES); + glVertex(center + axis * radius); + glVertex(center - axis * radius); + glEnd(); +} + +void Composite::Draw() const{ + ObjectProperties filled = Property(); + ObjectProperties outline = Property(); + filled.outlineColor = Color::Transparent; + outline.fillColor = Color::Transparent; + outline.offset = false; + filled.offset = false; + + const HenocUniverse::Composite &geom = object.GetGeometry(); + + for (HenocUniverse::Composite::const_iterator g = geom.begin(); g != geom.end(); ++g) + ::Draw(**g, outline); + + for (HenocUniverse::Composite::const_iterator g = geom.begin(); g != geom.end(); ++g) + ::Draw(**g, filled); + + glLineWidth(1); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + for (HenocUniverse::Composite::const_iterator g = geom.begin(); g != geom.end(); ++g) + ::Draw(**g, filled); + + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +} + +void Line::Draw() const{ + glLineWidth(Property().outlineThickness); + glColor(Property().outlineColor); + glBegin(GL_LINES); + glVertex(object.GetGeometry().Origin()); + glVertex(object.GetGeometry().End()); + glEnd(); +} + +void Terrain::Draw() const{ + const HenocUniverse::Terrain &geom = object.GetGeometry(); + glBegin(GL_QUAD_STRIP); + for (HenocUniverse::Terrain::const_iterator v = geom.begin(); v != geom.end(); ++v){ + float mu; + if (v->y > bottom) + mu = (v->y - bottom) / (geom.GetBounds().bottom - bottom); + else + mu = (bottom - v->y) / (bottom - geom.GetBounds().top); + + glColor(Color::Blend(Property().fillColor, Property().altFillColor, mu)); + glVertex2f(v->x, v->y + Property().outlineThickness / 2); + glColor(Property().altFillColor); + glVertex2f(v->x, bottom); + } + glEnd(); + + if (!Property().outlineThickness) return; + + glLineWidth(Property().outlineThickness); + glColor(Property().outlineColor); + glBegin(GL_LINE_STRIP); + + for (HenocUniverse::Terrain::const_iterator v = geom.begin(); v != geom.end(); ++v){ + if (borders && borderVisibility && v == geom.begin()) + glVertex2f(v->x - 0.5f, v->y + Property().outlineThickness / 2); + else if (borders && !borderVisibility && v == --geom.end()) + glVertex2f(v->x - 0.5f, v->y + Property().outlineThickness / 2); + else + glVertex2f(v->x, v->y + Property().outlineThickness / 2); + } + glEnd(); +} + +Line *Terrain::InsertSouthBorder(Space &space) const{ + vec2 begin = object.GetGeometry().front(); + vec2 end = object.GetGeometry().back(); + Line *l = new Line(vec2(begin.x - 0.5f, bottom), vec2(end.x, bottom)); + space << l; + return l; +} + +Line *Terrain::InsertEastBorder(Space &space) const{ + vec2 begin = object.GetGeometry().front(); + vec2 end = object.GetGeometry().back(); + Line *l = new Line(end, vec2(end.x, bottom)); + space << l; + return l; +} + +Line *Terrain::InsertWestBorder(Space &space) const{ + vec2 begin = object.GetGeometry().front(); + float top = begin.y; + float x = begin.x - 0.5f; + Line *l = new Line(vec2(x, top), vec2(x, bottom)); + space << l; + return l; +} + +void HenocUniverseI::DrawContact(const dContact &contact){ + const dContactGeom &cg = contact.geom; + vec2 position(cg.pos); + vec2 normal(cg.normal); + + glColor4f(1.0, 1.0, 1.0, 1.0); + glLineWidth(5); + glBegin(GL_LINES); + glVertex(position); + glColor4f(0, 0, 0, 0); + glVertex(position + normal * 40); + glEnd(); +} + +void Object::Insert(Space &space){ + space.push_back(this); +} + +void Object::InsertFront(Space &space){ + space.push_front(this); +} + +HenocWrapper *HenocWrapper::singleton = 0; + +HenocWrapper &HenocWrapper::GetSingleton(){ + assert(singleton); + return *singleton; +} + +HenocWrapper::HenocWrapper(){ + assert(!singleton); + singleton = this; + + width = 655; + height = 517; + center = target = velocity = vec2(0, 0); + zoom = 0.5f; +} + +void HenocWrapper::InitGl(){ + glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glClearColor( 1.0, 1.0, 1.0, 0.0 ); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_LINE_SMOOTH); + glEnable(GL_POINT_SMOOTH); + glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); + glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); + glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); + glLineWidth(2); + glPointSize(Point::Size); + + Resize(width, height); +} + +void HenocWrapper::InitOde(){ + world.SetCFM(0); + world.SetAutoDisableFlag(false); + world.SetAutoDisableLinearThreshold(0.25f); + world.SetERP(0.2f); + world.SetContactMaxCorrectingVel(1); + world.SetContactSurfaceLayer(0.01f); + world.SetGravity(vec2(0, 0.5f)); +} + +void HenocWrapper::Draw() const{ + Clear(); + DrawScene(); +} + +void HenocWrapper::Clear() const{ + glClear(GL_COLOR_BUFFER_BIT); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +} + +void HenocWrapper::DrawScene() const{ + float tx = round(-center.x) + 0.25f; + float ty = round(-center.y) + 0.25f; + glTranslatef(tx, ty, 0); + + for (Space::const_iterator i = space.begin(); i != space.end(); ++i) + (*i)->Draw(); +} + +void HenocWrapper::Resize(int width, int height){ + float cx = width / 2.0f; + float cy = height / 2.0f; + + glViewport(0, 0, width, height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(cx - width * zoom, cx + width * zoom, cy + height * zoom, cy - height * zoom, -2, 2); + glMatrixMode(GL_MODELVIEW); + + this->width = width; + this->height = height; +} + +Color::Color(float theta){ + if (theta < 0) + theta = 360 + theta; + if (theta >= 360) + theta -= 360; + + if (theta < 120){ + g = theta / 120; + r = 1 - g; + b = 0; + } + else if (theta < 240){ + b = (theta - 120) / 120; + g = 1 - b; + r = 0; + } + else{ + r = (theta - 240) / 120; + b = 1 - r; + g = 0; + } + a = 1; +} + +void Color::IncreaseBrightness(float factor){ + r += factor; + g += factor; + b += factor; + if (r > 1) + r = 1; + if (g > 1) + g = 1; + if (b > 1) + b = 1; +} + +void Color::DecreaseBrightness(float factor){ + r -= factor; + g -= factor; + b -= factor; + if (r < 0) + r = 0; + if (g < 0) + g = 0; + if (b < 0) + b = 0; +} + +Color Color::Blend(const Color &a, const Color &b, float mu){ + Color c; + c.r = a.r * mu + b.r * (1 - mu); + c.g = a.g * mu + b.g * (1 - mu); + c.b = a.b * mu + b.b * (1 - mu); + c.a = a.a * mu + b.a * (1 - mu); + return c; +} + +void HenocWrapper::GetImageSize(const char *filename, int &width, int &height){ + assert(sizeof(int) == 4); + + std::ifstream infile(filename, std::ios_base::binary); + if (!infile) + fprintf(stderr,"unable to open '%s'", filename); + + infile.read((char*) &width, 4); + infile.read((char*) &height, 4); +} + +void HenocWrapper::GetImageData(const char *filename, void *pixels){ + assert(sizeof(int) == 4); + + std::ifstream infile(filename, std::ios_base::binary); + if (!infile) + fprintf(stderr,"unable to open '%s'", filename); + + int width, height; + infile.read((char*) &width, 4); + infile.read((char*) &height, 4); + infile.read((char*) pixels, width * height * 4); +} + +Texture HenocWrapper::LoadTexture(const char *filename){ + int width, height; + std::string path = "../tests/textures/"; + std::string fullpath = path + filename; + + std::ifstream dummy(fullpath.c_str(), std::ios_base::binary); + if (!dummy) + fullpath = "../" + path + filename; + dummy.close(); + + GetImageSize(fullpath.c_str(), width, height); + char *pixels = new char[width * height * 4]; + GetImageData(fullpath.c_str(), pixels); + Texture texId = LoadTexture(width, height, pixels); + delete pixels; + return texId; +} + +Texture HenocWrapper::LoadTexture(int width, int height, const void *data){ + Texture texId; + glGenTextures(1, &texId); + glBindTexture(GL_TEXTURE_2D, texId); + glPixelStorei(GL_UNPACK_ROW_LENGTH, width); + glPixelStorei(GL_UNPACK_LSB_FIRST, GL_TRUE); + glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_TRUE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, 4, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + return texId; +} + +void HenocWrapper::Step(float delta){ + world.GenerateContacts(space); + world.QuickStep(delta); + assert(!world.IsCorrupt(space)); +} + +int HenocWrapper::TextureWidth(Texture id){ + int width = 0; + glBindTexture(GL_TEXTURE_2D, id); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width); + return width; +} + +int HenocWrapper::TextureHeight(Texture id){ + int height = 0; + glBindTexture(GL_TEXTURE_2D, id); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &height); + return height; +} + +void HenocWrapper::Blit(Texture texId, float left, float top, Color color, bool flip){ + float width; + float height; + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, texId); + glGetTexLevelParameterfv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width); + glGetTexLevelParameterfv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &height); + glColor(color); + glPushMatrix(); + glTranslatef(left,top,0); + glBegin(GL_QUADS); + glTexCoord2i(0, flip ? 1 : 0); + glVertex2f(0,0); + glTexCoord2i(1, flip ? 1 : 0); + glVertex2f(width,0); + glTexCoord2i(1, flip ? 0 : 1); + glVertex2f(width,height); + glTexCoord2i(0, flip ? 0 : 1); + glVertex2f(0,height); + glEnd(); + glPopMatrix(); + glDisable(GL_TEXTURE_2D); +} + +void HenocWrapper::Zoom(float factor){ + zoom += factor; + Resize(width, height); +} + +dJointID HenocWrapper::Glue(Object *a, Object *b){ + return world.Glue(*a->GetFlatlandObject(), *b->GetFlatlandObject()); +} + +dJointID HenocWrapper::Anchor(Object *a, Object *b, vec2 p, float mu, float erp){ + return world.Anchor(*a->GetFlatlandObject(), *b->GetFlatlandObject(), p, mu, erp); +} + +dJointID HenocWrapper::Anchor(Object *a, vec2 p, float mu, float erp){ + return world.Anchor(*a->GetFlatlandObject(), p, mu, erp); +} + +dJointID HenocWrapper::AnchorAxis(Object *a, vec2 axis){ + return world.AnchorAxis(*a->GetFlatlandObject(), axis); +} + +Rope::Rope(vec2 start, vec2 end, float length, float tautness) : start(start), end(end), terrain(false){ + const float erp = 0.01f; + const float dx = 20; + vec2 dir = (end - start).hat(); + vec2 delta = dir * dx; + + RopeSegment *previous = new RopeSegment(start, start + delta); + segments.push_back(previous); + start += delta; + + for (float x = dx; x < length; x += dx){ + vec2 end = start + delta; + RopeSegment *current = new RopeSegment(start, end); + segments.push_back(current); + AppInstance.Anchor(previous, current, start, tautness, erp); + previous = current; + start = end; + } + previous->GetFlatlandObject()->SetCenter(end - delta / 2); +} + +void Rope::Insert(Space &space){ + for (RopeVector::iterator i = segments.begin(); i != segments.end(); ++i) + space << *i; + space << this; +} + +void Rope::Terrainify(float bottom){ + this->bottom = bottom; + terrain = true; +} + +void Rope::Draw() const{ + if (terrain){ + float minY = std::min(start.y, end.y); + float maxY = std::max(start.y, end.y); + + Color color = Property().fillColor; + glBegin(GL_QUAD_STRIP); + + vec2 v = start; + + float mu; + if (v.y > bottom) + mu = (v.y - bottom) / (maxY - bottom); + else + mu = (bottom - v.y) / (bottom - minY); + + glColor(Color::Blend(Property().fillColor, Property().altFillColor, mu)); + glVertex2f(v.x, v.y + Property().outlineThickness / 2); + glColor(Property().altFillColor); + glVertex2f(v.x, bottom); + + for (RopeVector::const_iterator i = segments.begin(); i != segments.end(); ++i){ + v = (i == segments.end() - 1) ? end : (*i)->End(); + float mu; + if (v.y > bottom) + mu = (v.y - bottom) / (maxY - bottom); + else + mu = (bottom - v.y) / (bottom - minY); + + glColor(Color::Blend(Property().fillColor, Property().altFillColor, mu)); + glVertex2f(v.x, v.y + Property().outlineThickness / 2); + glColor(Property().altFillColor); + glVertex2f(v.x, bottom); + } + glEnd(); + + if (!Property().outlineThickness) return; + glLineWidth(Property().outlineThickness); + glColor(Property().outlineColor); + glBegin(GL_LINE_STRIP); + v = start; + glVertex2f(v.x, v.y + Property().outlineThickness / 2); + for (RopeVector::const_iterator i = segments.begin(); i != segments.end(); ++i){ + v = (i == segments.end() - 1) ? end : (*i)->End(); + glVertex2f(v.x, v.y + Property().outlineThickness / 2); + } + glEnd(); + return; + } + + glLineWidth(Property().outlineThickness); + glColor(Property().outlineColor); + glBegin(GL_LINE_STRIP); + glVertex(segments.front()->Origin()); + for (RopeVector::const_iterator i = segments.begin(); i != segments.end(); ++i) + glVertex((*i)->End()); + glEnd(); +} + +Catapult::Catapult(vec2 center, float width, float height, vec2 hinge){ + arm = new Block(center, width, height); + arm->GetFlatlandObject()->SetMass(1); + AppInstance.Anchor(arm, hinge); + + this->hinge = new Point(hinge); + this->hinge->GetFlatlandObject()->Property().collisionMask = 0; + AppInstance.Anchor(this->hinge, hinge); +} + +void Catapult::Insert(Space &space){ + space << arm; + space << hinge; +} + +void Catapult::Rotate(float theta){ + arm->GetFlatlandObject()->Rotate(theta); +} + +Point *Catapult::InsertSouthEastAnchor(Space &space){ + const float offset = Point::Size / 2; + HenocUniverse::Quad &quad = static_cast(arm->GetFlatlandObject()->GetGeometry()); + const vec2* corners = quad.GetCorners(); + vec2 v = corners[2] + vec2(offset, offset); + Point *p = new Point(v); + AppInstance.Anchor(p, v); + space << p; + return p; +} + +Point *Catapult::InsertSouthWestAnchor(Space &space){ + const float offset = Point::Size / 2; + HenocUniverse::Quad &quad = static_cast(arm->GetFlatlandObject()->GetGeometry()); + const vec2* corners = quad.GetCorners(); + vec2 v = corners[3] + vec2(-offset, offset); + Point *p = new Point(v); + AppInstance.Anchor(p, v); + space << p; + return p; +} + +Point *Catapult::InsertNorthEastAnchor(Space &space){ + const float offset = Point::Size / 2; + HenocUniverse::Quad &quad = static_cast(arm->GetFlatlandObject()->GetGeometry()); + const vec2* corners = quad.GetCorners(); + vec2 v = corners[1] + vec2(offset, -offset); + Point *p = new Point(v); + AppInstance.Anchor(p, v); + space << p; + return p; +} + +Point *Catapult::InsertNorthWestAnchor(Space &space){ + const float offset = Point::Size / 2; + HenocUniverse::Quad &quad = static_cast(arm->GetFlatlandObject()->GetGeometry()); + const vec2* corners = quad.GetCorners(); + vec2 v = corners[0] + vec2(-offset, -offset); + Point *p = new Point(v); + AppInstance.Anchor(p, v); + space << p; + return p; +} + +void Terrain::Insert(Space &space){ + object.GetGeometry().Finalize(); + if (borders){ + Object::PushProperties(); + if (!borderVisibility){ + Object::Default().outlineColor = Color::Transparent; + Object::Default().fillColor = Color::Transparent; + } + InsertSouthBorder(space); + InsertEastBorder(space); + InsertWestBorder(space); + Object::PopProperties(); + } + Object::Insert(space); +} + +void Composite::Insert(Space &space){ + object.GetGeometry().Finalize(); + Object::Insert(space); +} + +void Composite::InsertFront(Space &space){ + object.GetGeometry().Finalize(); + Object::InsertFront(space); +} + +void HenocWrapper::setOdeW(const Whstc &p){ + world.SetCFM(p.co); + world.SetAutoDisableFlag(false); + world.SetAutoDisableLinearThreshold(p.lin); + world.SetERP(p.erp); + world.SetContactMaxCorrectingVel(p.cmv); + world.SetContactSurfaceLayer(p.fcs); + world.SetGravity(vec2(0,p.gravity)); +} + + diff --git a/FullHenoc/henocUniverseI.h b/FullHenoc/henocUniverseI.h new file mode 100755 index 0000000..aaf70f6 --- /dev/null +++ b/FullHenoc/henocUniverseI.h @@ -0,0 +1,305 @@ +/* + * TT102 HENOC + * 2008 @ ESCOM-IPN + * Implementacion de HenocUniverse + */ + +#ifndef __HENOC_HENOCUNIVERSEI_H__ +#define __HENOC_HENOCUNIVERSEI_H__ + + +#include +#include +#include +#include +#include + +using HenocUniverse::vec2; +using HenocUniverse::Dynamic; +using HenocUniverse::Static; +using HenocUniverse::round; + +namespace HenocUniverseI{ + class Space; + + typedef unsigned int Texture; + struct RawColor { float r, g, b, a; }; + + //vector RGBA. + struct Color { + Color() {} + Color(float theta); + void IncreaseBrightness(float factor); + void DecreaseBrightness(float factor); + static Color Blend(const Color &a, const Color &b, float mu); + Color(RawColor raw) : raw(raw) {} + operator RawColor() { return raw; } + operator RawColor() const { return raw; } + Color(float r, float g, float b, float a) : r(r), g(g), b(b), a(a) {} + Color(const float *f) : r(f[0]), g(f[1]), b(f[2]), a(f[3]) {} + union{ + struct { float r, g, b, a; }; + RawColor raw; + }; + static Color Transparent, Black, White, PaleYellow, PaleGreen, PaleRed, Red, Green, Blue; + }; + + struct Whstc{ + int fAnim; + float delta; + int cmv; + float fcs; + float gravity; + float erp; + float lin; + int co; + }; + + //Propiedades visuales. + struct ObjectProperties{ + float outlineThickness; + RawColor fillColor; + RawColor altFillColor; + RawColor outlineColor; + bool offset; + }; + + + class Object{ + public: + Object() : properties(defaults) {} + virtual ~Object() {} + virtual void Draw() const = 0; + virtual HenocUniverse::Object *GetFlatlandObject() { return 0; } + virtual void Insert(Space &space); + virtual void InsertFront(Space &space); + public: + ObjectProperties &Property() { return properties; } + const ObjectProperties &Property() const { return properties; } + static ObjectProperties &Default() { return defaults; } + static void PushProperties() { defaultStack.push(defaults); } + static void PopProperties() { defaults = defaultStack.top(); defaultStack.pop(); } + private: + ObjectProperties properties; + static ObjectProperties defaults; + static std::stack defaultStack; + }; + + + class Block : public Object{ + public: + Block(vec2 center, float width, float height); + void Draw() const; + HenocUniverse::Object *GetFlatlandObject() { return &object; } + private: + Dynamic object; + }; + + class Wall : public Object{ + public: + Wall(vec2 center, float width, float height); + Wall(float left, float top, float right, float bottom); + void Draw() const; + HenocUniverse::Object *GetFlatlandObject() { return &object; } + private: + Static object; + }; + + + class Line : public Object{ + public: + Line(vec2 a, vec2 b); + void Draw() const; + HenocUniverse::Object *GetFlatlandObject() { return &object; } + private: + Static object; + }; + + class RopeSegment : public Object{ + public: + RopeSegment(vec2 a, vec2 b); + void Draw() const {} + vec2 Origin() const { return object.GetGeometry().Origin(); } + vec2 End() const { return object.GetGeometry().End(); } + HenocUniverse::Object *GetFlatlandObject() { return &object; } + private: + Dynamic object; + }; + + typedef std::vector RopeVector; + + class Rope : public Object{ + public: + Rope(vec2 start, vec2 end, float length, float tautness); + RopeSegment *front() { return segments.front(); } + RopeSegment *back() { return segments.back(); } + void Draw() const; + void Terrainify(float bottom); + void Insert(Space &space); + private: + vec2 start, end; + RopeVector segments; + float bottom; + bool terrain; + }; + + class Terrain : public Object{ + public: + Terrain(vec2 start, float bottom, bool borders = false, bool borderVisibility = true); + void Add(const vec2 &v); + void Insert(Space &space); + void Draw() const; + HenocUniverse::Object *GetFlatlandObject() { return &object; } + Line *InsertWestBorder(Space &space) const; + Line *InsertEastBorder(Space &space) const; + Line *InsertSouthBorder(Space &space) const; + private: + Static object; + float bottom; + bool borders; + bool borderVisibility; + }; + + class Ball : public Object{ + public: + Ball(vec2 center, float radius); + void Draw() const; + HenocUniverse::Object *GetFlatlandObject() { return &object; } + private: + Dynamic object; + }; + + class Point : public Object{ + public: + Point(vec2 center); + void Draw() const; + HenocUniverse::Object *GetFlatlandObject() { return &object; } + static const float Size; + private: + Dynamic object; + }; + + class Wheel : public Object{ + public: + Wheel(vec2 center, float radius); + void Draw() const; + void SetTexture(Texture t) { texture = t; } + HenocUniverse::Object *GetFlatlandObject() { return &object; } + private: + Dynamic object; + Texture texture; + }; + + class Catapult : public Object{ + public: + Catapult(vec2 center, float width, float height, vec2 hinge); + void Rotate(float theta); + Point *InsertSouthEastAnchor(Space &space); + Point *InsertSouthWestAnchor(Space &space); + Point *InsertNorthEastAnchor(Space &space); + Point *InsertNorthWestAnchor(Space &space); + void Draw() const {} + void Insert(Space &space); + private: + Point *hinge; + Block *arm; + }; + + class Composite : public Object{ + public: + Composite(vec2 centroid); + void Draw() const; + HenocUniverse::Object *GetFlatlandObject() { return &object; } + virtual void Insert(Space &space); + virtual void InsertFront(Space &space); + void Add(HenocUniverse::Geometry *geometry); + private: + Dynamic object; + }; + + class Beam : public Composite{ + public: + Beam(vec2 start, vec2 end, float thickness); + void Insert(Space &space); + void InsertFront(Space &space); + private: + float theta; + }; + + typedef std::list OList; + + class Space{ + public: + ~Space(); + size_t size() const { return list.size(); } + Space &operator<<(Object *o) { list.push_back(o); return *this; } + typedef OList::const_iterator const_iterator; + const_iterator begin() const { return list.begin(); } + const_iterator end() const { return list.end(); } + void pop() { delete *m; m = list.erase(m); } + void push_back(Object *o) { list.push_back(o); } + void push_front(Object *o) { list.push_front(o); } + void mark() { m = --list.end(); } + void destruye(){ + for (m = list.begin(); m != list.end(); ++m) + delete *m; + list.clear(); + } + private: + OList list; + OList::iterator m; + }; + + void DrawContact(const dContact &contact); + + class HenocWrapper{ + public: + HenocWrapper(); + void InitGl(); + void chingatelo(){ space.destruye(); } + void InitOde(); + void setOdeW(const Whstc &); + virtual void Draw() const; + Space &GetSpace() { return space; } + HenocUniverse::World &GetWorld() { return world; } + void Clear() const; + void DrawScene() const; + void Step(float delta); + void SetTarget(const vec2 &target) { velocity = (target - center) / 100; } + void UpdateCenter() { center += velocity; } + void Resize(int width, int height); + vec2 Center() const { return center; } + void Zoom(float factor); + dJointID Glue(Object *a, Object *b); + dJointID Anchor(Object *a, Object *b, vec2 p, float mu = 0, float erp = 0.01); + dJointID Anchor(Object *a, vec2 p, float mu = 0, float erp = 0.01); + dJointID AnchorAxis(Object *a, vec2 axis); + static Texture LoadTexture(int width, int height, const void *data); + static Texture LoadTexture(const char *filename); + static int TextureWidth(Texture id); + static int TextureHeight(Texture id); + static void GetImageData(const char *filename, void *data); + static void GetImageSize(const char *filename, int &width, int &height); + static void Blit(Texture id, float left, float top, Color color = Color::White, bool flip = false); + static HenocWrapper &GetSingleton(); + protected: + Space space; + HenocUniverse::World world; + vec2 center; + vec2 velocity; + vec2 target; + static HenocWrapper *singleton; + float zoom; + int width, height; + }; + + void glColor(const Color &color); + void glVertex(const vec2 &v); + + void Draw(const HenocUniverse::Geometry &g, const ObjectProperties &properties); + void DrawCircle(const HenocUniverse::Geometry &g, const ObjectProperties &properties); + void DrawQuad(const HenocUniverse::Geometry &g, const ObjectProperties &properties); +} + + +#endif diff --git a/FullHenoc/images/Botones/Config.png b/FullHenoc/images/Botones/Config.png new file mode 100755 index 0000000000000000000000000000000000000000..13bbd81206fcbc04bcdffa757c1a4b2f3128723a GIT binary patch literal 61916 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 D)6QI) literal 0 HcmV?d00001 diff --git a/FullHenoc/images/Botones/Enoch21.JPG b/FullHenoc/images/Botones/Enoch21.JPG new file mode 100755 index 0000000000000000000000000000000000000000..9e0889891edb61bf4d5aceba53bb9c63c7fed7fe GIT binary patch literal 16857 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 X!^Y7{f37RX00000NkvXXu0mjf<>q_D literal 0 HcmV?d00001 diff --git a/FullHenoc/images/Botones/My-Documents.png b/FullHenoc/images/Botones/My-Documents.png new file mode 100755 index 0000000000000000000000000000000000000000..ababfb26d9b97a09e919291171328e93beaecbc6 GIT binary patch literal 65055 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 UbkJj`e*gdg07*qoM6N<$f~lq_L;wH) literal 0 HcmV?d00001 diff --git a/FullHenoc/images/Botones/SSave.png b/FullHenoc/images/Botones/SSave.png new file mode 100755 index 0000000000000000000000000000000000000000..59c53630dbcc4f04424d082f19570d297fed77d9 GIT binary patch literal 24370 zcmWh!Wl+^m8~xpT;nLmRAs`Kc)TKkZ1wpz6l$5-*(%s$NBF&{sNT8v*(=WySl187CJdP003Bu3No4i0QzqU0#Kp<&FpTeXa6B2Ye^MJ0QfUM zqo&XMA0~5^)pyl$vU2q>{bC77S~!_m(kgy3wYJo>G`0BpdDv1E0F3(-WhAvd7yIqo8U$jpPpWImJFBZLGhOa-R03X>8pp8*=D)KY zT{YvZ?Hqe=j7AxHn3^A}tP*l|d7`!E1?3&a3iEL|U?Q*4Z*J&S=`wUo*?(;L04>Oq zhJcJz`LceI>@qU2x6)FfXfyICy^c z(>pm1ar<-JgSuz5Qg_m zVGXy^<+W9}-5-{J(MJ7%Gh~EYl$+u=2*K^IamR56NggfxD~cZjfqC2K)XsyS5i&Dc z_1_O&Y`otc3ZF&?<0C%5#_61HjW?9{f}_;yaea=aIAm%5M)fE88{_XPzVK!rd~PR_*64qZ-O-W;T`Y>J!8s6cOf zDOgBN2T77yi}LVH^kChe@#OxwFT`=dFM5(sheKr~+9^Y?p%Yb6K0@>=oc)JE70Y`A zgAE1rnpR;B%^X4rq(TnN{gsvMW-d<7o}C?Q$>yxA)w=!LBTv;xhCE&e@5eTUfxcPY z3L>5OA3Yf8X^GBF#fN5jnzR}r0&L=>T1Ws`N#PJhmQghL*qficc8=(*NZ9-JhFk zYip9N^vH(-(o9&~92iI4jWqZUeA>a&c@g!+$iH7@v^p>7RZk*?Na9T($KlnfCnXPe z4s{u}VzLmJJmGR{#AfD8p>4nZETPadb2G=~apo6yH6@~SF*`D{^tAM}b#xSY?sH%E zakpRK*DLY$y);i;+BIOregXsKEA4^$dwqS}+v{sLwmpuC!0BuY_0YYqCMG65O~-Yv zZ^i0<{J1uE^|@Gbc7GaCf2(6__P#tL`Fg*%4U6)Tbo*~!!y3{{D*jopwQyX?n6E#w z$%Kz_M(76c96vDMXvES4DbcKRMOTf(3K@$)QKQdCW&7rNvDKf|@m2E$8expo9ppI~ z5i)#+Vk}yg%SwM7?sA>$_{nII)358R#7(>aR~fWjh*+-v=s4rncQxg7SL?Wo=5Edp zRtSF#NGwYTyFDG)gmO;A1`A*oDf~km8(M0~-T-(1)*_w>oyq=w< z{=R{dUrlsW84yrK0LkWEQ%(-OF;kz>!5^6SP+W~Xf)Lk5nV_d#@`6Iv2##=x+PbUS z#}HadmPo91XeM)IpwJR)X`XdM8=_P65OSH&RU3CnyzJCl@eB9$5w9J= z88Qh6p&k(X(fz`4ma%T2UrTHO;e$)Ef!spNgMl#A8^|v|RMC<}xn@9|J1$ew1ZZWp z5lSMvn2$G+h*kb>n;_P$_7UK$4?|uP7wecyv|^O}xxphh=;=cMD&%{wcCrW9Pj?8` zQpba*FL7B}kuIiFra-#%6QZ7-cSV99mO4&R-jIpno`9VP4ewg_TK=qJr8*W(y5gmS z-N!v4wqDrLU*o`0%*A*6{|?`sxu8fpF)m(>eYroonz<^zv{u`{*oCpZq1{a@{sR#` zUGr>nWXJZ(%cUvRX1jaGv5`1|wKYWoctu(9rd2ICG^tn2lB2)?b*J$Uj}>4=~45QGe<+Hf5g*k;9mFguN+?gYW1A)=>vo3b~%wM zZDAClP7z@teQc}{6Qz(dtv`xC(i?1|(!u<2`d}eIvp;}>Q3{tymX=dnic*QTDXEl> zzfL86-gswhywciw@NxZ*zn+&^;KOC$A#4%MwZ5qS(1NEXwxHi6O#Xj*9#~ zN)Y$4*MzW&<(n1PAjZb*>Gj1Qx27H?mTosM!jVf17sP9#QKTLVR@j{n8`Co-zbi`? zn{TG7VoWzv+D!JKX^p!!>bMq1PRs3}k9=PJ|Rx3X04JF*F^$MdQ&qNYpvqko$cWwA&hN>OD+=FWZL1pP}0 z!LzM7)``>4T!SjaMuyYvkbAw3tF|b>2z6{SINLr>KIOJcq8EzZWLfaMOPB5A>2nr} z(%WDJQ!{djztG!owaW;n290-C*$a2s(J=voJ#hn;f9ch3XZP)?A>kI#a#8U5v40H> z>t3@y;FHhmyd#oT2eyOZ{A8{gKMyoAn)8tsBE4b(Xw_}IKZ{p&<4?hj=ca*W5lLD) z`5yu8DZ%zwpfw6?P(Zh{Gh0av!60eKi}nmEQlRx)o!uuSa%aLtuuaU@1ICJ)9DZ?9 z`7UImtV05N#V&a?_^;gq+?;$dG!l|`Xb0pC@2h2AF|F3Gzow-f+3%+N4&lc2A_S?E z0~^^QPq*ryVk0Unb>ucNUK7QiMjz$qHwM*}a^J zUIa}+j-&~#lEbLK6Ybw5@Q8fh?Re;W*6&;#+Gd#p@TO)@JHxiOEy;_2ds)zjNUg`! z2_?i7lm$`g+Uj#hoHGp7{WK~swSy*+!A*!^pT07fKf)BZN(IQ(dinVHxJ5)TTwGkZ zB_vLhL~$qU29Xhdw);_Mu|40ke!M+8Y5buLq{@yag;9R|cv9ZVtzLbgHS&h8J3(*A*oU99NnM;R!$$1}KUViu`)NTm1M z9Ket?rCBTg#Z9YBoA|)3`xM8_&aWUnhl~JN6Wj5tHAwc2ie1s#g_wgT_3ubw+7xxI z8)JU{xFd=0lLbbY&}sX0mgEF})t(RM0P}rd|x>-8XvnNjnkU1cRP@%U%&wOa}rJ(vx06DW(iZOvM%{%84+?K%~-LX=d zPo5#mKGy3OvxiE!Lph%^{?YjBclDyDmO!9jWFi`9$#Yk8|!trb|z0Z_`scDX+)(6*hz|!5;W!-Ht1(s*2$&sC!O84=E zqn|XV1mlH3T#!tFdQ-0=)P0)WMSd7#&ERO!tY#F`IpSKjiJQs&Q~ku|_i9C?f|}KD zq9%g0WPqCHqZ)8ieflPxK6>J0r6xJ3jlBx?`qKzRzd#2J#RnsyF%F>Q`%4lKta@Hf zq2!-WvE*=1p?u^&S1iB>52$z3|7M$-VJ_4z>_T8H($BXYpS)d7Tj)qzSXfy(x)uUw zsCvzv93T7aTxQX%-urbvGKvFYJA(uOwFhwEg+k6w4vSg zXCyZDJdKc1_p$5ksbycqk97~u6xoVu5}t7lS~}6tqs_Ya6g0_u6TEzBgsr|M$}D2p zl1&I)1q-YP;c57u2F7xFRz=m=QyQ)K@yKk>ty}rA z!R?lz6UyIp9~;agtWLiTqQ>3+b+KkYf(K?qW-={pOvBW?a>I9*sPaz;3c%N*ejNEE^RgRM0acBx{NVmu{L)S4tHVfmgC#UUKlsB;<0o<=po9p`i1^k@a^Ka&3#2?Q14LZHUt{xbivSliwnIVV*m$f2O*Q2veg4uH4 zjf{p>y9|U^&c`1-KyyC`b)GH|##v5Q!EBCLxLgfsn$Kc>_n7_}tCZE%bFRcGS6V*W zT&ot9fo&GOp2{!<#EE`C<5C{&<@Cw>-d`i{OO#hp04UiU+MBaExr)ot zVkgoHIMY}pP$q=r{P+u+@pxYWkyc3_9q#rwoTWitnhdpW<4hYC1=t+Bk7B{5b>B5I zNxKEe&RCn#X!FNH_UmMOH%+`SPpL5BRinV1=}q__v1#cqK<>{lH{^M7&w%weP5wY| z6gXNOLJcAZfMVGKX{t%K=H~0VSFkJ6iU4XL+pDYf7(4Zd^v+Mwgt3}WoyNjw5~q@x z2c(AXXro8q+#Ku^TR-5s7d|NvssFi02~g0lY?oarE=DvYJrGk0exOh!U8Ch0Bioo+ zt1>(oFasn+wqtYiE*9-UER>uv>Znsz15XEj)$TQ=f?p&p*m*$mIiPYpgw=V_aV;wT zLby0@f7mcJwMQAOJ+463}Sb{$YTtpr9bHF1w4~`&YoS5Fk1DyIpBm%=IpFv8`p{ zz2?9GCwprXD_0i%bW63XAb(Ieu3%ZPlyTNe#pB@*b|S-0{3+5X2rcI8*X7#Ec!_hY zs9u<7Jygbh9#movyl-80l$W{EQd4OyeSAbcJB*Xct4!QLXWoBEvG@c8i0u8iMMafI zhK3Y+i2~Q%UoUG?vGS|XGXGQH)VB@2`!%UPsWtr-qGEs9TlCK0_2S>Tuo3?xJoNk# zW&8%D^16U-N!RWAPV(1a_}$)DnGPoKe@uXeQL%`2>h53pC;Md+<}A84iFRhxn}4~= zs;cG1x{`NU#K08J1S$lwN>_uwZlx$;hBV8G48#Ee>O zu5&?v5KOZ7qN~#Qp*aOc;P`#}&C_kgO7e;fAbOKcm(=IC*2YE$UHe1-?;G7-C|f!v zEt%;M0ig_5Jl9aYLNvT~L?!8+J`TV+VN?1*x*eYQAtW7+XIC~d5>5?pyjFePkXed- zdEH%KKE|u%1;iGnOHnYWiVS10*kaZku|Zl}9exVNj?opmmJe6~qeytVG2}m3A{BP% z{GhU`htj~AU_T071W5&!B0D-X*7`-^&5!(1pbG~kSDLh5lI*N&5tj7Z0=UnW3S`D0 zw*5{2t{YBh1!v(!C?EjT$v{8ZuUybE{5)Q)^np3p#wE)kyF7ASzEtI}f=~ME;=rHB z=*>UbE4_{iKXJH~*#kjWt+=H9dMhu+%o)eMmMr-OlC0Q|SYo_Gdq=o{OmX;ug4Kc$ z=12OU9EwKfsdST9WG39unF04~WQ6`YHH3f`bFbeL_51Jlsd4`TN_}+cvd4F<&TNCG z9@fcGxh2s+z|f|tda(sP0UW{do_zF#C1EU-A~F>yBhQ`u-WL`tx$VTu91Lm}yWNfM z?9N&-NV@%4qM%~d6@Jw{u$uW3eR^VNKubm34~6&g%10ei!* zrn|?wHf)>Yhk+4i?F-~F zpMb7UTQ4cY7O+>HXdW_O)uT7(kkw;0yFARi$2W-kxf)uk{x-Q^Ijv@M5{1jv>8tCW zRW$5QK4tmSpYJa`G%jMe?JworCZ~vZb4m1WdlcfTVW^qTELWc8j6+dIdmMSzEGFL) z_3H(Cn5bU>+HbQ~5Q--lXEOt7X=#Se9cH#J9)4PN>>u|I`U|k8u9crf-A!+wdnZgq zFI&c=-aGeYzFcT2#BzsY?459Nx843hhFir_&Td`W4m4->@p^UzK$J48N62oSzgE<}ortqoQeq7R^t{Q@j#(3i?c?n&bqnX9?r`1?iYWVep>Q>X7;Z4bpz*k zugf3fNEj$knSpn<&LKLk&rkP~4ZN%qnSR7nV$Z0%K?v~%N~{KSde6>@Kx4ry_1loJ zn1T{Df0UME_rpI=OSLRrx*rAL-_K-)y{`6P^`YA{Wm+}|zuWlNaOyZm@m=9&qfi#) zOB%|W%Bq6f-IoZz%R{}%%ZV4@p`)bZ!R{l-q}+^KbE0^hZ)oxqko$8RhIu2^{0*nx zz>FQ#8_y2`%*=hIEAjUi7;gdi9y->*;k54(&FL%9wj>#F%C_|I+8;5D9v2OT{io(< zoLB!H-@3;H5P?G*YM$*uF(UN<`)ebLUgqNx%lgO^M~ML#*F=nK(>C z5I7y1F*(mC7}mro&$p~u7=h|@$F4R~6xt4e8 zl|1oH0!9!VZF{ZBXZ#mJ*`(9)=k_R*2SKi(q|{r586MUil=*d__DNACpjF;am7C}F z_$f3HZRi~Y>Ai(^dQLD{vgs2J?j8nl)i#LT*4j#tAIf?D_Bj_6o9JW=hcor(eOVY*gAB>ROLZ1=U4K7ASaACl zkW+EsQt5Pel)Ma9v-7_I*j4$*8_!#_{Pm6_B<^qr#yX`tn{RMBpXlN>=Swh*BP@3D zFs8VdI?)wtP3|{g{r0R75|n6Lp>0;~JyVkkGQyEjx`+)I){M3%fZI4%!eIU2v(gbb zP_&zihJgFLo*bV=Dy3ItBert{*DE!&mITj+y{Kr3x5YzghgC2yWtCDPEET_b0JDAb zJG7JxV0z0;7Nhp84k*O8=o|fQ5wkEx1msm-cttJ}Pi#)vE%WuTU35-ngfsbRGgj-~ z8&_vA0F5>bdC_#1>l$?@giMhg;)Ru3+cKZ5d*_;Egp9u00D6xx^kr-!N)4I25i1e5 zc7hoPOFxzJ!ql35u*4<50<`HGMwhX!xYBVJo;B!sd)E2l@WAF*I)!WYEgV?##8d&; zgJ1=-7~I=L43nGyq(Md6Ml0G@j0%J9-7gy1EBXZ6vp!{qrbNxSf+z zI~JH^ZQvG<{CqBCMlBcEUgsnd*6n|4eH1m2i-OBFmF|Yl&(637UI%i5_n31L#KpO@ zhu7X;2;-`YA~n_up&%cEZmsoe8h|bFK=a`AsCjQt;@&oWFgABvAzdUfU6L9O;`+(+ zCpY_MzMJQ5#?D%|G`~Lb)fzoU)x5i5z;9{i{BPdFn>XeAYk-Bq)AMMG@o(p>xEZa! zYY+^55|Hz=CnX&yeYvjXJ?XMWkjPMD?*%!Gmq)xXkwnxR5yY$9c?ZDwvz`_q!RMLRkOu-OL z)Yi2{^TFsT^SWbe;V)YXsEM}idLMnZkdBdoxunhcQiC$DXanRVQR^y$s>zk^fd^(^ z3H!sty^|fN(emZVk;B}C&2ACsiqgx7l(P)gYq)V}y~SnMW|Sc?A>5XO*YZH zN=Gh^t!f_HuLJTJKZCHvFD~1{_}b;8+z*&sW6@{VP`+hhC#fqxJK>fiLeiFR6Ovy1cAeQRShJL zAeSVuA47ns&CJ>hCzSHSrA&nCal(zfOsfJRFpogOP5z2RfDy8&s%?28A)5-YTYxvD zbZsH$D|@B%4EIWt4yN3~U(*4XM?%|k9_>DVO3aC3ya=gA?zQdNMh-;^2cle1pkr?O z^6Mw#p25jqH5uX)q?$kWPgP74BCMDDuJANIF_yVXP`ZmeRYPN$$FE{2sV9D&A1x zUInInvQ9^gWVf30A2^2VjYUW7L(K+0S8#{$uy8M86_V%X-+g*mWh>p(fbo{1&%@U; zjE$rs1&M)LqK{{o;;_EbqoR`Pycb%bi1Jxy-?Nc;Rq(0j?kra^t)J>!|17}8c z^<{fAYW=_RR){>AJ#u*I2}4>&JQ%1A(FH8ZPQ9E19=#7bp>92b`OS2^G zvy1Ct*#0L!QA@6eb(lf&e&4c`Djv58Vy5j33}hpbgUv4WXEFY`CN133V)+;x$%^Z- zCHWl50=2z{PgpAlbIt!wTS;ge_M|>qT6kCN|1!$DPx~n`Ag11JtMuL-CrT|6FGZ-} zTCPJ-l=rveslm2Vm7w@J%W&0zEo%)K+cW0;bdQ2k${(K4Yif?0|7n>jvhACCU^dsUAEf8|1}(_wJK zhRdn`?1)`Sj*`(F?btCvg)Nu?nYegZAsRgV8Lg2UwV1tl0~fRtz@~&bwF`tea@BYU zINTum?wahA*qKjX5)wAIW>^0mKvqU-zT{1K5-}52I;)sGky6F6+}fXq{F2DlSr^^u z@mWAe2*)$W+qRA3pY`qX?kV9-3jY?PM8>5GA$FyoV3H_dIyT=YeYY%(d-a<0$$n}0 z@7ins+iVEJSKZ>+N;a4sQd0@!Dg_Pa63h{-w0387^?rI{lUR{7Dh?-|EWsIpi+FaW zykH=`=LHqrS33dy-W5Lt+g6?E@kUgd5+XF!UTyR=)@Z*DYsu`MbDf`*P%LP@vk1v_s8^7D9g%EiZY`DaILyg$g zK+N;4Kt^R&kjv{YNAo;i=8QaM+dtHb-ESpWnB4$*%SG#-ZG}F8Y~!rt^0&ybS7 zzf%SB1tX>TZ_6uV$-~76ck^iFO7%3iYfL*3IoZPRs9!cn0v=4oVXI%AB;!6OdyCgC zQHgr$jHbtw|J0aufy25v^N~{gLChx37M_tKE4Yo2HPnV2Q(iXnJf$iMN??Q&rDF0e zCS5D=&G`Ap>f<-wCk}(xlvbTz>5?jH?O?zxqP_IW0VQ$+J!St#B&4<{B|j2_*xQ}1 zbh&*m7?*KUOxe4@<|;t_bDDlC8Wm3f6p1YjQamgSf{8=8H)9Sgbw)KHArVaGGT$i) zy@rraG+J;I*W6;_xQp<&9`j2H2T&-v;ZOPQ6|&e=tr?kLZ_U^|8|vH}FrYfvez;g( zBFoYX05w0?!vm(bICmh&HxeZr5UCVIY6wd*hC2)s@q!!$r|jId#seas20c3e#mE%1 zCVYJPb?T$|2JCG#chd{Sulv$`aLqxed}fd17cTm5w&X`C(;;X7<=yFXHURd-oN=*) zn6MJ)V%hRTUOy>42b|W>;rJ-p1{v!h9H&lo#8WEc2aqt-`LFzT)+9C3NrJsEACgjI zMa+gh)hdNfbSxqbxZz8^*uhp5tt$&_I6%w(R@{USUA@Dj3EY&-qPH~%2|&$2rv*Ar zQR%|+(*_3z`xE*8dG*%=#4LgUwsh=Rl}df>nc)4gykE%O63V|TC-JL|kI&Z)TSqOo z<~U}(K=sm(xERQpJ9q+(Z51ejbc_PA1~`amYtZQJn#P(GAs+a2F^${944JEZvKI@ z8Dx))133Uz_cgH9aRRSf)HxAZG;tslk~*T)Hh)J# zPhtEOjc3EltC(D3KU*kVh&yR^RkV&g z+yb0NGi2s4`JYr=3dW;YtNGa^s8fLBe(!1Z4KlL=lf|4|i0kOGKc7C)^oqwS^nmfO zvl6yImHD(1BFns117gQ(&l7W6N9l}yas0YdB5=+JL;kHs^pkloB zi3(lOhLvRCtBPcO!;-QjRm$_g#S8?M{z6OS{k#vY-;n)l&y~AWyqIivEYGu5!CcYv z0o09aL-S1tHeYrnTp0;Vo}GSAg|bHmqzd(3VKc(uO`vbob41B5sFwQrDpW2OkYM^d z6Jnp&XMSX`Q`s+c4(VnP*-HAi3OsLO>qZQ^K8VAppw+)n`4@Ckk*F7-%?9NbkoO*d zHk?g}#~FZ~TBo`8YD(nh4Cl3Vhn8NDS^9*K&axk>LYXw`(3iGNV&75F@{lGHLDdsT zRDfFBW=TE;qMnKavJhInOAh?l$zZi9?^1kF5k&G_9e5}E(-kO{^QfX$?s31zlr`at zs<&F4Po*nV8Vfdc+c(*E0Fs$*901IW1EUHeLxjgC_^;8_6BD&*>97r%! zRSxTO%n>!a6dQroYShG4L=ng=g#`9$=B-thnFcu?E$e&Ib1euu?L`S&0+`4RzjnV{ zcVg$u{K`e*c9=~4RN+I!j=&2|k~2YX2I?2%n*{7|n7t)&0pvR$1;PTjT&}p{6ov!z3`*^7%(gMGY5G zl?`78ZrLf(8k<1qac}dH>E(#j-)o)x>Ur}^;mZNC#XO+Mt1g?LD>r$}iLlEL&s+Zx zP$gTg)^lL%NIQB4ttZWe%0%O(s@HJ5Nbf?@BG(;TnX6qt@Z_`VpF+ zHn6zFS+r2tOd%2Ek{sE0aW4N@Jwa`t4`1`vl!a>P=7GdXA~C|uuyH?h*%M-Q?L(*) zM*Oyp)v>$xTTdWXn^kC4UVtdPJqxSF?<8Rxy5o(Mey%_Tz~h?R;&%%c2S9c~lNLx; zU+mG*riOCa|FgJ-Kj+>pA$Zx=+^s|NAK!<$zW>0)x~5LTMw^DC&MjG~&FDPC;HKmB zAeM0X8oiLl{l<$aj5 zhi*N-{CZ+hW&UgqAth!u5ysKC6h}BJU(|N|M>+?-M4c*2?fH$n^Z~B`i^tM>fR`#< z-DCP6GVo>hy@jEn5yK=hBsO1z9U^w!3;Z7%~J)u0KI?sXz^KyG0FcYYe8>c5MN_!tVhIR7ifRw zUwEy2<%#el6@)J^(fucIum?Wt>FW035sRqHH;0w`;sE=v4R&$!qV@7h%c*EQB#Z9JUTj3P`R&1 zCY?bSEC(pKafx+ETuhPDA;eZR*F`DD#NO2+K{)|#p)4#cNDRs@-90$zSs6styGKiz zrQuj$DD8(Xzm7w*bobeUs~_&O8po4+=-daW@W5N<{_tJn?VESR2-a%Ml#6L7YpK21LTcOCz!W=@Lo|eY^#&L<(`{oD_ zg9k!OOhVGH0qU~~^X|rjS9JZ6Oxb-N`}J$9Hu^PKMsSjt%qNLEp?V~b(<%znK5r+3 zh67YC@EJ++@1AyslZf*ge9yuA_Jl~P%f-)_d*JE{Gc6~FK)K2P<(U(fnCoV;FAofx z$M|-7-e*M$2Dk;ic_TPuG6_FKiuVDUaqZqqmt~5N`O66TuJ<6H-`-!6j9kxX`si;M z8;k*O0%*-g-{RU$$Z6K8SFL_#nu(0ywZ>LIa1@%C(V@Z4tFON}cwc%$VsqilN9nL$ zgAFUKbig=v&r8;FmzgDWEdY-oad^e!9kv*i6R=fmF!-X+UZ@nm?JCGbh#r;`+a>+1 zKeZ{hiY-tu@t1n7i8~>7OL^ZnD52{2w3)EA_``l{spUWJvf#3Dn!uvQtoch$yR zF$I(Btp>R0Y~%<_EEyZ)Kn5FSv6CL&X`8R?*rwmy&I)% zo%*jgxDjlj`Swo76oME&N3rM9&VPuN;n zN%qEpnIS)O-*3}1*-{qYGvt`C7Nhgq35ioeB$S&|meU;n7zd|Q8L@-`BLSM64m|#E zr`{iaJXp9tt6pHpN5`K2oAbXxf|obt+T|_Q!}Y~y13}fs9c=N+t%+e#6+mvXc7P=Q zUO*OIDG}-3!UCFr)gNX4@E^%N(fS;kuTC@G=(!wllQeXhzj4O(iZqzRkCjLsAP25E zXG`?lz9}eHd~vU@A}5IG{5r+=`_BK)+AI9-VIZ`qk-T&m_qQmB`FSBxi#PIIuN=zJxT~qpj1IGv+n&1`VCwJlk~JXIoI!R3XWs;> z%RI{DN=`t)`|1B41)cznbi!=t`#VF%m_BdM;v%)pNQLDwEXr6B=}$Va#(dp)0~NVA zZz2SK8qAh~`xD6;x@o$AQ{?IN9Y(o$1BDJe4us#&$m^UBf)`UToiFZW>kJ->l9$0P zxZa={m!X*eYHFvd@V|YYNF+(*Xhf5503{xn!TfM`Q)|;fCP*f|O+Ics;=kbA)WKDC^Gq61W}LuhA_-!pnLsiFZA6q8VCk`1a?KRrExTt&Uv z2foP)k4guCd|y$CZ$Cc&-H%OD0QaGYCprH!&OqUdj?=A$Hn$;i;Vd;UFpaqx07k5= z7QQ!rSNsmru^3wY>R`mP&59qtXj})zg4rerORh_?#8L1)v2KpAy4Cnl69u{YP=vYM zhM?cEIWAy+@az=n_5X%tgh68%Q?C)f>DZzN%ICnAwa>O9^?UVB9Nd)pE|x$**-PBa z59G$rr$C#bX`g3x$0g@`?48Vp7|ow+Y(w^SwnhbV$rUiLx`M+2qFAce%B#|)m?icpkzU~K!?`}XwrfIhxM=DFAL|uD$d5~Ze0L4k{wE$o@2+L zE#d+7X1_ij06VX5SxBck0BgW)9PB;WYunP_!EO$k7?VwYxE5{EP-Xz(iI!pGjJfve zc0je1xl3Twt0b9?7Q5#tHyk~_#R^VU89{i+Dm$TA0E^93Q^3$?Xw5lA=6%bi=Gpvr zVwD{Qpn>2lh?l2DM=X;Wro1M>mZ-0OPgct4?G{?*Di0(l|~@5e5%UB3ZMSOb(oX`&4i03F5~? zRU-3$rSpmcU7JGPi@6!=ppO6(xJK4b$XPY{)?L3w) zNDcGZh&~FzpdH0ecD%ttaA!!_S8mopW|*hPC%~>u(8m%9{|^jkBnbTD-GYriB>sL1 zu3iZyk20AhVM7Q`hY;WGYi!;ne+JTJYeJ!5CAV=vB#YG~*&1LLeHG3I^8^u}Y?FgKzqm8AH5xkFKeSsQm^y(^lNTjljVw-Xf6r4qQ=2! zkn#Dnv3gYhf-k-@yy2$0;t5zGiR*Rr=68MreRK|^{v2`o=5ve=@heIUAOX&Ft1{_Yah5sc5dH5gQ-kbAZ z`pgi9$QJOwiiP>L>qvc**9wTK93YdUEzQ8$BP(~eKpN&R;Uq>GBIi?R-4P;{5dbW% zeQ=*qY@Uu2V~`G<7Z=lQVpJRq2V1djV3_;aPC0mq0;Gv%bhnCmb^5Z^wPR^HU-)Bp z06RHq@Jgly)6{+rv!R{2;1WniOuJ}1hW z@67*q>C;u+=2HZ~ineEAdgjTe9-R%&2OZugVy#2_-cDUjfO1NG8u^W3*SUgXfdeW) z%b(qBhu=i^t=x*=l02Z;eG`~pTGA%DPsdEE!FhNV>*Qj3{cKDMdx_`iTsf80Sd(UCggp~{5Jh=G1Xec54^*jK@-;L(9N^47Se&aje46V0|U>2o!0_d=x5kvx3mPyrfP%Jjdz6)t*=o*a!9kjnO6i1D zbsl?I(Q)aLrPuj*dItY5zMI4&_@;ciw*r7^&hM=Mb&X^&Jt&?Jmg}d}-n~-4%cWVB zh3c~;L!>0px}EaRq$1e@)&_Zqmyr>_lVs~QJ7ysdCHW}CyQ=Na|4x0cf9)?whhZ;c z99UUd6))Q~J>4%Xm^eC)JN^7iZH@U>M5KX&HkRzH?z^j187f06>wqXdbbf%^pE#=1 zurPlLR!m0J`g!WNJJVgVocsgNWFS!|aSJ|7|NVR0*Bl&~JB@!f#7sF*1JOEr>xL(O ztPkB1zf6JrY20ZLU<%R_$%3iOoD6$qW#zkL-M=|mS=ZK9#jmyw)G$lW8qz>sA0t!rIvPFK*$zDS?oV^4ekv&?$6$S-j;3psfdvc*k5AKU9Gn6%Dk^sY%vZpw z77VrN=TD0NXeuf$Yz!%O#!cnFv`0`77(OM1XgqVHkeuG|rWmJlP6x;Vbg=x9Vj%8M7jyjT8HcA*G%x>JV1r53H4_jq<4l8RSG!7yI=}FXO5aISg%UZ zz0KgvIPE-hrqw+&qn%K}JkHA(V3IDE5FHb9os`wk@uOu}=VQfQ>t{z;jPz zdN}b5(zO`svy^dei5)}jT$CkfcS@c{#pr1xf{S?})GjgsDGD2E)X(3NvVPf-1}I(d zbq=T^NR6x{Cs9qDIar%)87O|lx}}rc56mu6@lz6S1R{yQ3A26Odw3@-D!~>05^!I_1WPm1ReDMErLv zJC7Nqk13M}tDL>RmjAOyKa34-AOS%5}W?r+l*Xr>1 zP2_d?Q$<~3#gp#-M25i*RDL?W6$>0hB3C1@Vpzvx`jk~@r#aAQ7K{yEV=fP<7=mUn zDuE7dD?sX1z71OOpOx)sZFkjGkV}0!sql;ZujPGv4N_`WlB9(D3DS2|a-=Q8@iK`U_Y#z9%idl+2m} z+=4EaK3Iwn|B_G{KvPyGG($yW2F!J^|Haa~y`ZfOFIR2F>Y;^U}X!gI{fv?E2 zb#aN>Z0YXR@m1ML^-INR*UZOa4DNnH2vD4A77hZmlzI9A_^bZ_fFS%204CwDib;Y0 zDzq?x|0-TV0khHRu8X{clfjy{(KNhSGWp)hn3FhGm;Ax8G)l@op?}DAd~aXzM~ycr z_mk^zsuZmjDp3`Bg{+WasqM*{UT>3h-KENNDb9xvfHZ&1!cis8BXf z*{a)P+c#Kd+V3H4O(Av!!)0E;P$l`rs#OFz%N?Whu15l~+jZ8CLn`v`*xIS%{(&a& z&9_q%2qH}4LB#*_^%zB$j+ODva5A#IwaEV)gmUisSk~t2%tKkyHI?q?2gRXtPD{b& zB{b-WFcvpPr9gIi3~{t*l;K=DFY%A^YxevOso5tzWsj#}4gaNC0@mK0JzY*TOuzY~ z`*L)0u~CBSzc0=J%R7hvBzAh~V`a94HH!N^2}yfvDZi=^J?|rMw%Ql`;A2jQ>quUU z{VqK=Qr%511;7OZxQ|BhTy@Y7gN4?kqwLO3ZdfO+n1>{*FXGL<-e|^8DJ19lr=Qil zhmD>I+PAV$2mt7~N#S0|n*JLu*)c74?N-`~rIi(swX+!8e5y0iC$@TYI;5;`$__Fk zpkfFT(>q#lKm{=aE=McEk@hKd-vhC` zULLVFH+Af#z7mr$oppwn&xCOREBRAC89 z&&j1L*Wr&d&eHcl`wH3|Rr<$nz#EJ)BoDV5?b zTp4J(=TA&d!lRGA)0;XJg1KXzfvW;IUdQGc0c!#P49k?rG}~nXGt=FWIOG4tn^!L` z-@5))@DkekQhofs`+kAa)(2I^K{f&!Dr|tH#9s+)03iSf2M`@OaR`3u-A};Ze&KU| zdt|H>*(%xuSTIsFGVyJ1dpjBm0+qm(2iE(e;De_EKwTIBl|vy#kIe*@M1Y~$Y+>XR z(t>Ze$F{_gU;fn(cb9H2e=vAGK`BE8sJdaS%Jr*j-8=>mp4u-&`N8w!)$D(eQ^MzBmV&?zi337ytw|0Ppua>%Zp(09qRc5Jt1!_BNb5 ze-SQSc^#Ur9%S30DUi=zzqxc4&b=y-k_BaK^|nGrLiu{keKKMyT<>J&FIifgio zgb4`=yb1{Dx8K;@gr|P~DcITRb2Fjao4cUE=4o>mj1O2|UWPk&?yzw%>+5T7dmUK9 z*eF0n*Z_q7PwNT*xQ-73mtial!mc1+uP6YdHKcLPhcGS?iSm7Vf(f42Nu&GwHUM|I zxv&5)zVuaSHJi|EH~eEQ4iYH*2m+_Nb8zM@KMFg21b|I6T6aW~5;lfi;RO(J08mo^ zC~zh1b<4Q^CtD1_0rkp#=qBybMuL&k_TI;u@@e{C9do&vR~8fic;Yst;tMM~;R9 zOB1zA!WJR+V36~VEYnHiX#p00f(EecXu z_T@cim}?gK{$2pQH>ig)2H=hFp;Fww+KWRZ7)Z@c5D31UFMtHXFFXqF@3Ck=KNPr* zDnRNz(M+ca0}6Tp#zo&YBLw%Tu{RptU?68#5^!aJk2>~WPFkWy2Wo5w!B~L6sOZ}O zd=&s{1^_f)|393%V2UZF@G;K@;P=*n@JoGsFv?G12O*%)fq`r<@I!%jgJ7UKg#t$b zfDZXe;97jJ`4s_@dOZU7Ggre3;_#<6;osZ74ra;007Wa2mK670D=Ri=Qm}P zHUR3;anK8U2n<4YfWW;+A$lzQT@o5*?1ch{1_Sl;2~2evK(_QoG&>3y>Tk-3VE_iZ z1C1LGl-@lU?=QOmgYSghhpNgT8x%#)zp=3{525WJcmPP$)ras5u>Rl3zbPYS0D%oa z=gaq~egFnkru_g4#fMPh5C6N$F*KBupvx4koYo5fipK2Axx!2_WppM0rUC?6e$?53 zq=-I+88BgvQ2iJ%pn)2JjAI~=Z>ft7fbu+Nc||=6B*D5>A>$#6*WN`3?yP^Xq_?w#okOd>p#`7in@gYn^G+b;7CbkLfL_NuN|cA zS3rO&9sw!jmns%S`FJQRFx1EkkU7UWjoU>NQ~^^=8AS=yRyFWU?&*CnfEX}PKc%@t z1*ix*;p{pR1E>`MXpDd~1LeY+RTm&^7?hi(95^J~4u~>)5Y8ju zNC`ozR!Rs3&Th<%9U80|3}8UOlu_9L((P7ME9}uxn5a zBX7N*Prhf7ZA4y{qjJ;Nf&ukhjLZfw^hCM`!Um8v-%mQ?%8fEF1E_mXre?SuiypgV z0Ant@gf9S%yeTK^RL2?g0aZgX^8%QA8pVC-2C!vtUS;MA za+W;8#~xH|K7bL}0HlX86cM1lTA{K4BY@^U%DM-lu>qv_O*C&~mIB+oq&Z>)V$D>5 zyrIA}mHk_Ugr;yRYkKgyHS~inocmY*a2h*0te__}WLue|YyfmP{D8cs?ka`=m!twXnOrJ)rVa_FRU(-ffD!WX74Bi{nr%d0 z_SlsKYe1mZhCqNoRpJ3gkQRK%nS!XGXHG5#a1a`-nF72T^YQ!kvvQ_EU&2CQW;1t=XQ)Xxn_)sa;N=#vfv zirN57?_00Bl~E_=lFKxkReo1iyb6Gy(qtsGW@7>9P|r<&qKE(-h5?wK&(tX!bEUD{>gAbb5#TYhxO70(t_(wmC&&a24EV#sZ)fv(gLw1G=H#r$W}&*VNS(t z05spos@NwM#zZj|V0U+yEvE6CI+a*~oc{QH!EH2#DSH`4MBaevI0cK&3$UIGO*5b; zEzt&0H32XJxxaOibMobywm!ExAYug!K!@1?s`Jc?0)V4O)8SE5cA1Q;4ZzfCG<6a| zW`W_FW^b57oJb?Uqa+hO@Bp9-1j_IH*Lo1j@&Xt^G|wy4xoB(vG`H~Qr366`kTjcE z4Qv2FH5S$U4@@=%H0;Knj|DJ7I39JP8Pd(s;SeERC6H7AQWq6KKg6-9|1=m6P*ngp z2tZKvI_^ik#C(0JMnDSLRD>MBL#4J|pNTAat`l)RzZCpMr zCpuW}|D_B>gjnuBr%nKn$IqW`d!^EKs|EmOKu-!R?Fvay(9xZ_n{5mSkV)0J}0282AIZO|9jbsy4$wR$KoWXS^m@A-N5L# zxw*+67$Ub38gsv;)IDMZ9Gz3nA9bvF0cxWHP}QYE+9?6 zH#(gTv|6poHS}?s!;s`2NK>s6*Yoe?e0_4x%UUpiFaQjxQ%PlE;THV;Kl~#T_}ST6 zXf~VdH#Rml7#N6IR&}3A$3#65M8q~S3%tItGc+0v|M$eXxGnB=Vq$`wGp;>#Ex;~w zc3xVZbD*v^fM6oeG7=~TrPu4hXFvBh0Jyki&IN+$nHl!2W}^wmj~(~JEAi)%F*ua^ zHi;ugk9cYW&sQ)zI|J=@n;k>7A_*koK9kmU+j$mYpqieZ_P4RV-EP+xWCWkd$w~hi z;WmP#IF83;hGga<3>SR92;N~MU}5b)yWPv1ED(rg)o3h0#;m}3)e5z&+`Y>dQ7&D+ zobxw^oUt%Rp13Jj0Nb6m|E}BZ7EDe|a-eb>V8e#)R5x^gGYD!D2yAa}huog(`ml?| z8eMc--mhC1pC~v8IN~^NBS6X9f+>k@VYd-B9E*E*_5+*{D5s{T*!q<#S2CsKqGf>t zh=G7SAAmQTaKD}^K;b7jdO!_l*%cu-VW$CDMb925LgYbgPiZKZ;tljPu z6BZAAh|4S{*ms1mBg;5?^eAj?ZAHLB1&*>llh$>BDbzX7?H8Q)$}6wHA3ghQaf&)b z`>}`xJ@(94-ElW&x^WIP0Dxwe)nbB53qR;IK^>ieY3~e4N%~ z?N#{N*ItHied}8}>PX9zd@lTWPXTaA#Gv*JAn8HV5yNk+U1tvkmG0FY?!BOn>f*co zUAS`jN@(9@zCB7o9ou16@b)fFiEWn7k{-}lY@ZrPofe$)RFJAPwuU@?hOSf@gN@jKYtljGO})iu?HEkNey5`hJzw)m6B3 z>5{*F<@MKL<<1>{`#azLuAg#k!v(Ml7XkpgapMLoEG&$jpbi(uLNlt$oB`AZ0C-&E zC?&pZ+B-cx!+L5I*ik1N+3$j53#tZ6OG`^|?bYk7Qt4$3Ont@cJ8Xz<|#KxjWcpqdz&zgnRey z@ptA86x)zOu9s3H8LOlL2nGQ4004~*a9?%2yjL#+0G@vOY1TmHbmnRvLofatSd9It zQ>Xmz;sBd7XU+sKqtSo|AAHc?#=%1GeeZi~4+`&k*H6L=FT4PoTU+codhz}?ShmI7 zW^9}Rf0nr3Y<2wS{J>it^adPRmLLC*=dhvPjV8MWj=;mgO5Q#k*fa@iYpd|zF8jbS z4SQ$;sFw|3*l_0kG$e`MUt}FFTChh#10EUyRREB;J?k&PcU`^P;)7IJgXW{Q%|n_E4W2F~jt`$|ViAZ}AjsY!T6J_7+_syhXbKK3}gdG#vX zxN&{2AfR40fQa}X5R0!6Hm3vtKwHAjP9L^+c0w@`B&Go*ZK}<6er~>$I##{l3{yB@ z#sQxo0LCafT-+*h^-efFn?;2h(J5fCtKz<#dLb@1%K}j|MHp)x9 z)D)pk!-UY;CDyUh1%=+?17%pKY*wL$gx-`SbhNw!h|M}p(pHCRZ-gfVsf`U_&w7ZV z4$@c^pxJ7%qh+W7@)&i$F_DZAKvL*HNp-Ax>9pIPuFsc5p^kl20RX3|4&rTC#LA$P zlVNu$j8gzyt-Jt*WO3ir0K#Wz5mx77B#1_mQ|PFwp2d>XdX`bgsuySAc&W0u!ESh= zPExa?Vn?h%C{sn5q(KBp001jw0IY9P(1XAxxWFRzl+(KsKgmOo^PMC&mMlGv*J+Fe z18}IrLD}++rD2UKyHM+%KhYya&?76qnOnqDn4IkX^MA{z0Q=O~T$(O2v+R~(1e}`W zQ$AtuMN=$58H|Hc!$j9PMsY3`jSUa%7tb@eUwUllk9@vQ$ z08P!@fCtp(#4OFU6ngC$cI>bXAVH`hm8u5)8v3ZmLIrRfM#p7;|FA-xXlPMX1wy>o z0sUAI0;yyz^&N9tgJpZdZ`Eva(1Tc*Rh`i>tDN4IM1B29LJcWB3{fm3E`+1pZ*Ylc!E@k{AlDM<9rDd?*X zcR6JlDl5+bcyN{wkU~NoD~M74g@(B*N2+H>XOTF zE9+vrWq1LA#_Fz_eATh_XHB$}I##`iIR->(@ghPEIn)HfI4pne$u!ze5-uO#?k&3! zPGt^Zkl*Vf8}5sa2F&L}&F^7>;oUO&lLiCo>tSVB*gNfxV%#auS8a`p7hvG9-k8y3 zjXqvf_a||286*IJG!-rWb;dEhU`vcg073uj!T`bmP_CdRA0k-~N6&W~nT-=;xF}dg z?3cNfvzKT^}iV!xetpZs=hpfe9pn zj#71-8;M1bQ*CSjI=^jMf|_D$$;KNr>_kv8W?(BxxUeCip68H^y+=m37Czf?0Dxwz z>8Su*4H#Ca<9q5DBP;bhIN|B%^BEYkykDjQ2qR~myZ}P?9fhFAo=q~U3^5%D_BJT6 zIHh4}MnDk^WRXmiFgvv!A0wb8n3U{E8A7Oa&oedjYInxBx>cKuWJaOk|?XjDR**b4Yz=2libk@ohPyF)*8vMNckL)CS$t#-5s%L$6z{ zHh94vRyLu|;npH!V*}U?22~MKNT{Q)Jjn*&wO^(J?%N#YlftB5+s%xkdkZwUP2Fx>3Ec+3w0j1 zR&T2}768y_Gz0f+j17RKsg(qhjcX+O%*FT(yV20FgJk+ia@ZnJ1qgzHSQIJ>|BSmF z!NZDhi*QmW1`y&v!qHquoHJ0aO-lj*TVepi3Tjf^07<0xxr}SlF`TJ&0Oh#YD1{)* z6RcFB&eemI8R>+ANJ&qkwH8{XN>k2nZenaNA6a z3;#)XnUVyRrhtH>5q?Jzc!vu-_8fh_?C|Z#l;p*10JgB8jz&)^uJcUv=Fk}E-cbUh- zF+Dp?q5nG2aJaDJg2@gy`92b(Rult(iHV8dU0Yj!Kc)1(Y?MBZCFElWMTdxbgEO4c z@&Wi3>sQGG!52K!^9xkW;@`0m0$D`ws8@aoOe|(32QI1Oy5Jzz=`$J)HXH#EFw<{(5)d z{6^V=TGWje2R5j)4+iB725K<+c7ej1qj5x0S6Hj^fh<6$3HjtzFkF?D31Y%a`XDt z4?XnoBcI>c*m#M| zI}b@v6IJ0(QDC_n+t%6uipCzu@6%=sz*2n}7MH(9g0LqsfPqByh2XDp!QbMbFo02d z0pxP&;<>NB>!+Wbx_NWq7002E#6O>mf*Q*PQiO-3+XtAj&MGO$22}tN#|~DiP{$Hk zUHrnBj0R*j00L1Xphf^wV@py8ph*DB-&@FzHLU9~o|<@GfbfJMMC$z;DKtNXm3-+- zf`3=)^*1FoAXBQYQbz;+&wu^Lw);@u{?J?B@q0Tv{Z9^M3>$_C`0qZO+l{$ocFXBq znGw*Eh6Beo7!9DM*Z{E6i~|D@0_dkzD|Kcm_Z@Uj&T< zfTSK@SoekCV{gC0g@2Rl{JW4d(Pn7?K!_i~VO_fLgFk-gt&e=sq2%js2RR>oPvyjN z>+J&R)^@@(w3Oxs+1}oUJ9k$-y6gVt;?lBzY~j|TzkYl9j_>x9 z&mRi_fb^_NiGr4abwy>LGk{P+kQjQO#@IsbPf-DQyiA|JOX{VLivs}JvM+l4buRQ` zS(Ehq;il&r769a)o_}AsGjN#%FR%f9oEYge^ZIM&zBGU6=*6S=pZSRVdr7|4c$SdY z2;=Rv8#YXII?&(V++MxAvPq{WCtbb0y;qZNG@4B~cJzq9ed503{`zB&K9Z~-kI4k4 z&8FKldVTj5(?UX>rmIKV6M7#a3?Pr)Aze*DPZ2K8z)>rvvjGT~-nP`^-;wD4Mpgsg zp4$Kv3#Y%V+*!KeE`KtA=*XG*!$;pjsnZz@b|*U%-FY07>GpDVtKFHv(V6Z#X*OAw zjcjZ{L1x4!%rG<4opi3facT3_^XG&+WS#3u{lkZkzTKq&ZD&k+*8{--G(%KQvy@5qJw~RHUJ8NFJJWdDEP|~-QO-t@J9&%!bKraY@KiSR_@$h zbC*{k7zoi&7D0>TPZ>~b6Z#J5(&GQ#79yxsYa07MsPNzfm zbe=do1pw-0 z6r{vc)J>kba|~`Au8-eYTFbv9`8myNjiu zF$#9m1%rmWv=9(*9YFyRLLB2DAr^7mm2bNxj=LrFiO2bMZLuxDMgbN7lY@@G?&^|F z+?eWiZ5LP=@XgN6+IT4pMw*$KVf)(c7EHR}gX>dMQ?8EPthDgAT20=6>~W!^ovjC7 zYtz;Lx3+rp<*)qn&GYBZVXuAz0{{IoUl#f2T^Pcjzvm2q2B}4vI-@D#{)6j?rm@P);G7@j&gh3 za!FIG)0sqfS)r13K<=mz%Oa=zy4d}Z0D$}q#}NQR<*BEh+LPaT z>7|#{Aonz|Q5RcTR~d~8HYxx#G+;tB_7xqYX~ckz&PYpSu#o_Ufrnr)GVtI&mu=Br zueZJH7Gz+WdsSH#$@tvZ{${Jyn3|fNM#eueIW>u3h4?i;KiB%+|9<;5<$VyvCPh>> zdi=zx87@rZ(2--^&cuYZwYIu-;r#!ta{(5EbHu=$RW9gDoB^o4zKG--7yzgt0PIy! zfF=%b1EAY^ISjis;`qOq?uH2Yc_V6oy1vlp?XFsAD%O_*fb{jE<#PioJy> zwig}qiE}gC8JLGCLU=_co-GLga9QUkQUEZ3VM@)x1~lqaM8+UKjaRHN?#s>XcLhu_ p#S{YorkG-i0RU4>F=a2y{{!HG({29z+*beq002ovPDHLkV1k~!&qDwJ literal 0 HcmV?d00001 diff --git a/FullHenoc/images/Botones/Symbol-Stop.png b/FullHenoc/images/Botones/Symbol-Stop.png new file mode 100755 index 0000000000000000000000000000000000000000..9494d278453aacb3ac5348d8ef3635669a974d44 GIT binary patch literal 62578 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

|q--adG;OO6<}Clt-7AzQBVU z3{1aY%pA=kVwIe={W`x<+==Tl=ae*q*nqs5rc=2!gmNxVR$NN<&C^UhTEV_#S?h?Ku>@mAyjvN01LUy)+poD{m4=wn4z=ykF1E=c@p3N1AmMqV!a}I1UMhjOBADGA5G=-z@&lg zXS!%|^$q9?*;c>A9>$)vAQ=<~X?pK|kL@#g{Bx>I=Pl^eY5*Tk5Reu&nJaOA*fdQ5 zYk~LOPQXG6psP+E_DX%UH@DP9%V*LCCwIVrPEz6@eo%!QDLJe(-B-8Jr^jt$CR5x#?@6gPKN zkua(>wbU#SD8$8move}ml+sQN7GwLt=>u6rb}R?qN_fsoYF_$yZ>`gy+?rK=F?# zmM_-QBAwGHe&Yn3%Q8$(vhqOJ-Au`&$FEY=nXKb|>UnOu__>Oh5cO|CWkhggtaAQb zrR9S<^0La!QRRLZ1_bX<8P3I8qb$c$-4pC@(9h(&>52WF2<(rEC1_iB3t;^Bi2;mj iAMkDey|+oZ-91nZUUpgyQUn4pFRf>KYIQ2M5&r`dBzHCd literal 0 HcmV?d00001 diff --git a/FullHenoc/images/Botones/application.png b/FullHenoc/images/Botones/application.png new file mode 100755 index 0000000000000000000000000000000000000000..1dee9e366094e87db68c606d0522d72d4b939818 GIT binary patch literal 464 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 WW!ib57{HI@KuU6IvSrey0sjLYn~nAW literal 0 HcmV?d00001 diff --git a/FullHenoc/images/Botones/catapulta2.jpg b/FullHenoc/images/Botones/catapulta2.jpg new file mode 100755 index 0000000000000000000000000000000000000000..6fc05a6d1fcb55fae02df428a6764c0730d1f7ac GIT binary patch literal 7327 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 H@236-7q4S- literal 0 HcmV?d00001 diff --git a/FullHenoc/images/Botones/circle.png b/FullHenoc/images/Botones/circle.png new file mode 100755 index 0000000000000000000000000000000000000000..ed16c6e14468293eda6a35b81890739b121c5767 GIT binary patch literal 1359 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 BV}Aeu literal 0 HcmV?d00001 diff --git a/FullHenoc/images/Botones/enmarcax.png b/FullHenoc/images/Botones/enmarcax.png new file mode 100755 index 0000000000000000000000000000000000000000..d2fccf8486b9277d5e5a24a97c20d130facbc96c GIT binary patch literal 27118 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�A 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 c(m&W64j;ga7~l literal 0 HcmV?d00001 diff --git a/FullHenoc/images/Botones/fileopen.ico b/FullHenoc/images/Botones/fileopen.ico new file mode 100755 index 0000000000000000000000000000000000000000..c7ecba1a5eef7d4fe921013cfc649c82e2a2bce2 GIT binary patch literal 1406 zcmaLXc~I149LMoDd7&AKdE}8o8Da{u9*C%5YKr2uAPQ)nDB=ZcN*-ZmfTAnQf=8C+ zOq!aGAzp(APzZ%b;2H`ox(XuTptL};Fs#3)hnfMWKl=UV`0Thy1{##(9Q zW#e6{O(oQo7E@pHn8va)YRW6IJbi{zRfVDy(@p^tBHz&Bv11lJh`^Bq25|;=@)(DiDG2*ugen4|jzwq=A@pYvDe@Z)4`1;P(z{DP zP5LF$Z`lHgHlRjPgU!|AN)Zdr>iS*`XKbRr-%7iePutO$%FB7t4La|IR+Y4$` zg1^*)I*q`h7c`^@8V#b^_m$WX6XePDFUP6UvJ;v)y3sq$G5w6eY0(w zVerPsd*zY1=kv`WOBTtEq=!#aH$LvOsEx~(4qCxGqt}12ZdH))P*uOay<7X^Id*kq z*fj5v&RsgbZu{C0JcB<>h*&w(m-+PgU3W~SP-D! zo2b+7-r^q+uOAVqSvzaO)+D`FyWMXWOkEflIGN>(wK`qE)|O3f+w4!b&u-~L;$lDj ftNrG-TzfQ?%`4S>^FIwauXQ_6I>~j}R0946FwY8P literal 0 HcmV?d00001 diff --git a/FullHenoc/images/Botones/fileopen.png b/FullHenoc/images/Botones/fileopen.png new file mode 100755 index 0000000000000000000000000000000000000000..b5218285e70669f91dd3dc69a7a275fa3a70da7e GIT binary patch literal 814 zcmV+}1JV46P)Px#1ZP1_K>z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBU!tVu*cR5(wil3hsCQ5eSOO?6ceVkltXP}9B z)qw}j#ryo;_nhYq5(os~^RS>d9Gu(2tGicUeFVl*!-L$b<^Q2Pj4Eq+wWW?2_%qKCepcBbY@lLu;z3uTpB zRx>;84*W@GSpZP|X$s07Gc*2u=oD1fA8|-28inwI2}cV?ajAF=$AtY5oGhKd0Pm2< z9k?bLTl$Z(#wnE61(b6~hlDEzWIgpFtsF?NB8WL>MZyWyG9aVE%IFQ<^Qcw1A?cjQ zy|-=@);W>)%!Ryqf_DMs;IzL22&^=**(+?+6+ihoYmJARG>%GXW@gt?!i)f(}8xr|WF+3ph zJA%U0*cd9uSy68w%H8=tL6eZ`_?UPA9liRqpS}N9tqW(wQ#d3t;y|t*@tGRzOjTmb z4k?S10fJKpaJ9;^B08Es&Dq2`V9?HDNMnJjX96;*9&PVOaX7mRvMy@?uqo*a-l-g` z@~4m)s}sL;)6n!8p^$5N^EWg#^~Eoh@@78?QO=z0`(H$4+H3y_J-7S2><{907*qoM6N<$g1~!i{r~^~ literal 0 HcmV?d00001 diff --git a/FullHenoc/images/Botones/folder.png b/FullHenoc/images/Botones/folder.png new file mode 100755 index 0000000000000000000000000000000000000000..589fd2df59475fd8fdf3d5d9af419cc996c24974 GIT binary patch literal 3910 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 UXAhsOTmS$707*qoM6N<$f}|T@^#A|> literal 0 HcmV?d00001 diff --git a/FullHenoc/images/Botones/ok.png b/FullHenoc/images/Botones/ok.png new file mode 100755 index 0000000000000000000000000000000000000000..e355ea91bc58ac30d32e1837711cebbb282d103c GIT binary patch literal 979 zcmV;^11$WBP)x8*_I57)`V{o{K*&-1%4 zevgQ7E#-d$*WAFhsKUcix4y5n=!lS)mO?Ia1{<6_C^h6gtwS5d&_*%5PCaSiO@}Z7 zi9sPBkn*-xY0;_GUc)rra>jn4)(5z8Kuy#u^@g?NBVU!#$2@QU;NOcxI7Q@-5++H#3*sQQu*xC;huPaq2ZLb)a z9vQPw&yKYh*G5reQK-}IQI&WQcVbv}?@8JvW}ccE)4!P>b_tvMXre}`ozixRWJ5A0 zeK9E($Ksgx^SsDEkRNtxmr|Me=8ADc2OM&3)m+$A7^z6b_><$q4q-ztOw<^q#%?=A zVoxH9KP!$I7iUB&QZeoPv`FqrhE3W`@eJbg;x=60^SHmD%x01IV_qb)SMKrL zuvw_;Vxrn8)v8syV7t&N@_xUxCK<^{9#3Z0y{b)-`Iw5Ek-SpYB&>JVN5UUn^rf&t zcuAAhYOHs`2BAgxwOeo3{uNpjnTsi1;8(01FKe({Xz!-ag>_jq3P1JG)3xt;El6P^ zZt4v@Q!A9K5`GYV>88`d_dVS2(7QsFu&fh4;c7-XX;t?1p6i1S2&;rSYVk1czSXOl`bv03Sfbrzm6(1h*ZTwS7{^7<+AVzDgIa~v!n4}l(;2OIVCX==GvipUL8Wl4 zn?4qn3gyn5pb`&UK3e-z3zVx6nuViT9TAoYPia$NKJLSqfrodDV}-Cs_*8gDch0wrhpX|Hs7;Pt55ta+?_WPZ?E%ty zcl7GX<6#&ib$G%g0Kx`)KASRL^ZnUGrx zCTlifW8bF0Zh;X~MQ7g4Lm|_~ykU(Cppw z+zc^}2l`HK1P^+B{r2nc%R}+A|9PB!2fN@?ZS6gu^9@CduRn)+FfsrD002ovPDHLk FV1m%L!2$pP literal 0 HcmV?d00001 diff --git a/FullHenoc/images/Botones/picture_save.png b/FullHenoc/images/Botones/picture_save.png new file mode 100755 index 0000000000000000000000000000000000000000..777fb5d2e6a8418c573972246582c21d2c4984e4 GIT binary patch literal 755 zcmVe)^Pp+~01yB`w`9=u(Ho~0 z&q0|Zy>{}2gZl=9K}x9u0000cnYNkw{OYaOaSqCHmHYkvAYHn)ZsML%y6Mn?Rm1M4 zCbIimL%-gdGZROY_IT&jL)x4YKv_2wMMsgSixr3VuIl&xwpoodZKLe>9X|MwKbLly z@9mW{08m0kQ6yEY>2=p_>};yqiMnYtv8~_l+pdQ83=1I;07}RxQW8y8wNsO(vNNsJ zxzT2%smQIeNFo9Nln_CYl7W3j+smTUwfx(8yZemmMmgIvlXtBAH}d|cf5_39J9FWB zT>bgZGbKb&6v=F7!JU=6<_61l#uG1|c+xA2v%dWPU+)_++yuBPsf8gS*uy8tE)TNP^?wUn|FBDi_)Ep5))oIO#kS?(pBVA^IE&o0V2 z1UkY@NmW(0wrgcXNO*bi9DvTz4L^KUm{e2mUH(gvXxc2dSs)?^X1ZCKKmdStIIqc} zY8#!Ri;%QZN+O4dtZAm|x={|gX;Yrg9Xs$WDG3lp>Fmkfnp~qGh?EV=o(rGc zc5iE2*(=lK&%R!Iu5ROK1ORQ0xsI-E$A>rGws+=HNnvgLk2jzGKw)59)ygxcj-Lkr zfNySo7mq#2f!UFlj-4>yTcBWLvS9zN#!QbgB`G2R0KijcuYCLXBg5zC`|}3n1DZUQ la+ac%)7bB~m#l70{SP&gLHlD?H{SpN002ovPDHLkV1hOfWCQ>J literal 0 HcmV?d00001 diff --git a/FullHenoc/images/Botones/rectangle.png b/FullHenoc/images/Botones/rectangle.png new file mode 100755 index 0000000000000000000000000000000000000000..3a7d9795fd2cb912634e5882579ab3cfd132f116 GIT binary patch literal 690 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 HKk$D5thy0a literal 0 HcmV?d00001 diff --git a/FullHenoc/images/Botones/shape_move_backwards.png b/FullHenoc/images/Botones/shape_move_backwards.png new file mode 100755 index 0000000000000000000000000000000000000000..ee3f9b27ac50d9fb560d7f136c2fd1453150ced1 GIT binary patch literal 358 zcmV-s0h#`ZP)@H!pQI5+Ti_L0YJ|=5)wx zkF=1qGlSNkDr^3|$ws8mau%VAs!N}G7ur`j3{a)&lwiJCaRceu$939gbwXgD!G7W+ z`tnW&{YVf#zprcNBv=w4wLNbW#_u=%`)~XMSM_D9ZJ#Vp00000NkvXXu0mjfl(La4 literal 0 HcmV?d00001 diff --git a/FullHenoc/images/Botones/trash.png b/FullHenoc/images/Botones/trash.png new file mode 100755 index 0000000000000000000000000000000000000000..75dbb9fe98160ddcfb072a4b07eb2cccd7e997f8 GIT binary patch literal 66896 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

tnAG@T||J$p2(QZsd@Pp|1Ws*X$#2mMP*|Eqicm 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 Y@HWVbow@I?Ai#&TxV%`Eh+)wG12BlUegFUf literal 0 HcmV?d00001 diff --git a/FullHenoc/images/Botones/update.png b/FullHenoc/images/Botones/update.png new file mode 100755 index 0000000000000000000000000000000000000000..3cb8ba6c77f83d3977ecb2f80839a889a4051a5f GIT binary patch literal 7890 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 literal 0 HcmV?d00001 diff --git a/FullHenoc/images/Botones/wmp.png b/FullHenoc/images/Botones/wmp.png new file mode 100755 index 0000000000000000000000000000000000000000..b5133a42cbb320ab7b99b0fdc8759c40e0162a94 GIT binary patch literal 55610 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 Rt78BF002ovPDHLkV1n2%!>0fM literal 0 HcmV?d00001 diff --git a/FullHenoc/images/background1.png b/FullHenoc/images/background1.png new file mode 100755 index 0000000000000000000000000000000000000000..0f93c6bf420da2d6488601c5fa165c57206b322d GIT binary patch literal 112 zcmeAS@N?(olHy`uVBq!ia0vp^^&rg13?%;)_Xhwewg8_HSBC!}uyIG+0-%Vhr;B4q zMcmsnib4zwEDVnS>rI*jQ%d#ocHGE1#m&I*VJ;(s1M`#_uMbFCENlU4VDNPHb6Mw< G&;$V2W+Jly literal 0 HcmV?d00001 diff --git a/FullHenoc/images/background2.png b/FullHenoc/images/background2.png new file mode 100755 index 0000000000000000000000000000000000000000..1e293db67a5dfedacaee2b7448148dcf6bc53b92 GIT binary patch literal 114 zcmeAS@N?(olHy`uVBq!ia0vp^^&rg13?%;)_Xhwewg8_H*Z=?j1DTL*{Qv)7JF|TYP(;(y z#WBRgJNXAcD=$xgfffWhEMefe;e6zOFINv3CWtdI*jGH+{&<2xJWvyZr>mdKI;Vst E0PAKUcK`qY literal 0 HcmV?d00001 diff --git a/FullHenoc/images/background4.png b/FullHenoc/images/background4.png new file mode 100755 index 0000000000000000000000000000000000000000..9c1f3bfd77764732b9a0138ffbd134e696e9c6d1 GIT binary patch literal 96 zcmeAS@N?(olHy`uVBq!ia0vp^^&rg13?%;)_Xhwe<^Z1%*Z=?jFFzuE0?3u{ba4!+ qhYP)(Fbo8M z2lQrH7A8r8p993c@02?QX__)52*dDESl2Zd0R;qA-d*T+9EWqZZR5jP3hzWw#Nr?b z^v>(LV%xTAl)mpzv3w|sg30q7ecxvTAp{P?piDrw4bwDP+;!b0{+@Z;Hk_u3W7#2& zV;sj3)y!`nBxJs-s{h+qmPL(5U0%0A%=3&*)1b^}S#~*lo~LkK_vh2vZAhr7EKAk# Y0MNh_$Of+#C;$Ke07*qoM6N<$f{V6t!Tdm4?Fu&!p#^mymSVTh zU>sb6HlP;7W{N`X0QDB+k_jxAXelOsn#*51@U_g5OYIsl5L%VuiskzuK3zz?-u00000NkvXXu0mjfg_VD% literal 0 HcmV?d00001 diff --git a/FullHenoc/images/delete.png b/FullHenoc/images/delete.png new file mode 100755 index 0000000000000000000000000000000000000000..df2a147d246ef62d628d73db36b0b24af98a2ab9 GIT binary patch literal 831 zcmV-F1Hk-=P)R5;6h zl}l(-K@^6Q=FtdACA#^NDs^{Lp)6)L zgDB5eX;UdG_4H6F7*yIgXmIAu0!5NyOCtSU7G=!;6%|3j{gliox-!pOK?G(o&X({YLK$5)lC7F{VZbo703UCXA=&? zO>Nu>w#%A8Rp;5oKacEBBT*BGX+{#I_yE%2i8f!~SeeejbP6SvLH5VQ-~o6A-hwe1 z-+)oOG3-#N-p|7H3rxph%DcJ`E`ihFDtIo2X&L#)9#wa!-__Ey=>18UreVqnx(m*O z14e_~1JA$~|`2HhU^Ra!WRl)GgiU zYU$BXh#q3R4$gpm?mF#|Br$YH!gK>%1c$VCx82fBJaI+hxwpFb)g=^Dbv{zQc<7+k z9t8>W67osVx3S=)K2n#oseNr$I`ov*vgsc2h}xyrpv>h+JHeJUF8ZjWkj` zH1UE>PMpf&iLCQ!iioCJ)~Hi?YjvJaK8_pg59Au!plIt&?SDO~mzDzYD;xj-002ov JPDHLkV1h`%ho%4k literal 0 HcmV?d00001 diff --git a/FullHenoc/images/floodfill.png b/FullHenoc/images/floodfill.png new file mode 100755 index 0000000000000000000000000000000000000000..54c0dae237849b1ac4c8f74edd2a9f868df636e0 GIT binary patch literal 282 zcmeAS@N?(olHy`uVBq!ia0vp^T0pGL#0(^hKYq#uQak}ZA+G=b|8Hn$0P=tgS1)f1 zAd9gi$S;_|;n|HeAZL%Ki(^PdobTzqoh*htZHWsngj&el=~ufUw2W)>!)JbaM(j+t z*`p6k+Y&m*-QGCti{d?Yr?U^l-S&Rl_D1UcGDjwpO^yu-D(C0UdS}sHSJCWWm$KO) zBV53!M2U6A#7>?k0UbP7T%IVLyK`_xXR~pkLMPLcxv~ih^&I}cWcvG2i|^9E>c)Uu zOnvW}?6@m7=P=gCN-?L&nlLasiEenprD7hS5`JKk_KQi<6YeyC_#cY%WeVb>%WRI@ YUkl7o2x`3@5A+9vr>mdKI;Vst01-}XF8}}l literal 0 HcmV?d00001 diff --git a/FullHenoc/images/italic.png b/FullHenoc/images/italic.png new file mode 100755 index 0000000000000000000000000000000000000000..9a438b57ad4369dfd110d2f2bdc126890e1aec8e GIT binary patch literal 247 zcmV(sy*Prg8NDWuEbzopr0L)V>;{X5v literal 0 HcmV?d00001 diff --git a/FullHenoc/images/linepointer.png b/FullHenoc/images/linepointer.png new file mode 100755 index 0000000000000000000000000000000000000000..66933d43b30e4352c763cadb4f0ca3bd80325037 GIT binary patch literal 141 zcmeAS@N?(olHy`uVBq!ia0vp^(jd&p3?#QprHKJ4wg8_H*Z=?j1DTH@0 zC%qJfboH$su}j!iZuY)9>DRxMGyQkOZ#I9EdvfFM;~TQYiF#Z5)IYFal@OGE>#O_) PXgY(ZtDnm{r-UW|*?2%h literal 0 HcmV?d00001 diff --git a/FullHenoc/images/sendtoback.png b/FullHenoc/images/sendtoback.png new file mode 100755 index 0000000000000000000000000000000000000000..5aa3b0a24382610f1dc25d6708a02a90e3d6c340 GIT binary patch literal 318 zcmV-E0m1%>P)1A0e687B!#Lm%BmfS zjSWxldj>ac+a?Z_s+z6!20%orpk3GP?lruq{=t6%Y>e64N{5Y8+F*&ZZV(4{GllSW;AhrI>C^Mf7m%p9hk07*qoM6N<$f)Wjg@&Et; literal 0 HcmV?d00001 diff --git a/FullHenoc/images/textpointer.png b/FullHenoc/images/textpointer.png new file mode 100755 index 0000000000000000000000000000000000000000..b25832cada22845efb19650500edf1ceb522a8b4 GIT binary patch literal 753 zcmVE}0|Ntca&k#YN&Ni$6B82z1O%U-pTxw(si~<21qD}ER~Hu- z_V)G`78a(arl6ppU|?V*BqTmQJ|iO|x3{-VO-=at_`txxFfcF)2?^}%>>wZ@ii(Qb z+S>8)@eK_PnVFdx85#Nc`OVGE^z`&OIXNvYEjv3q5)u+aLqo#C!lb06+}zw%RaIzc zXj)oYP*70t@bCr(1|J_EbaZsFv9VoUT_GVMHa0d02ng!x>f77ffq{XXoSch`i*swgN1UC~rmJr6ZtwP$GZ*2`gv#l)X+8W}u^wiyPzo88jdmHTu(7OW- zlMU)k*g-2Zf^IIXHN!c(mF2ss+o!$2N6utNCw?(Ir*Vhc(JTP?=-L*|eis=D!-$m8 zUDdSq;cma*Ha6*!kgMyZ_X*pCVd~Gj?q|^)9YbKI(OrqVm{ZfZ@2PHF{gGgN-D*TgnTzabF6H?ke2Co}~y&DfWYwGHLUN z>ECB+w+ga*=wh-X*@Ob}V=m+b+3A^No>RkxNH1yW3PgHsQ;K9ZRdFD@*Sjvt00000NkvXXu0mjfuiIx( literal 0 HcmV?d00001 diff --git a/FullHenoc/images/underline.png b/FullHenoc/images/underline.png new file mode 100755 index 0000000000000000000000000000000000000000..9b8209f5252f74c3af386430d17b60f7eeda7ce3 GIT binary patch literal 250 zcmVVHb^x=zg3bp;R5gZF*!ld&eiecuD~aU8;zW%0@Dx(b}8DKI}F+qU^+sIF@nlx4|n z+j3P^(l<@R)>@zZ$QZ*}mWe4z62@Q7^L%gV1~VWy%?KWNb^rhX07*qoM6N<$f?^zL A8~^|S literal 0 HcmV?d00001 diff --git a/FullHenoc/main.cpp b/FullHenoc/main.cpp new file mode 100755 index 0000000..90f9318 --- /dev/null +++ b/FullHenoc/main.cpp @@ -0,0 +1,13 @@ +#include +#include "CfrmHenoc.h" + +int main(int argc, char *argv[]) +{ +// Q_INIT_RESOURCE(ftp); + + + QApplication app(argc, argv); + CfrmHenoc UnWin; + UnWin.show(); + return app.exec(); +} diff --git a/HenocCameraDrv/GeneraHomografia.cpp b/HenocCameraDrv/GeneraHomografia.cpp new file mode 100755 index 0000000..534dab9 --- /dev/null +++ b/HenocCameraDrv/GeneraHomografia.cpp @@ -0,0 +1,51 @@ +//g++ GeneraHomografia.cpp -o generaHomografia -I/usr/include/opencv -L/usr/local/lib/ -lm -lcv -lhighgui -lcvaux -lcxcore + +#include +#include +#include +#include +#include + +int main( int argc, char** argv ) { + CvPoint center; + double scale=-3; + IplImage* image = argc==2 ? cvLoadImage(argv[1]) : 0; + if(!image) return -1; + IplImage* med1=cvCreateImage(cvGetSize(image),8,3); + CvMat* ImgPoints = NULL; + CvMat* Points = NULL; + CvMat* HomographyMatrix = NULL; + + ImgPoints = cvCreateMat( 4, 2, CV_32FC1); + ImgPoints->data.fl[0]=0; + ImgPoints->data.fl[1]=0; + ImgPoints->data.fl[2]=800; + ImgPoints->data.fl[3]=0; + ImgPoints->data.fl[4]=800; + ImgPoints->data.fl[5]=600; + ImgPoints->data.fl[6]=0; + ImgPoints->data.fl[7]=600; + + Points = cvCreateMat( 4, 2, CV_32FC1); + Points->data.fl[0]=134; + Points->data.fl[1]=115; + Points->data.fl[2]=639; + Points->data.fl[3]=126; + Points->data.fl[4]=723; + Points->data.fl[5]=516; + Points->data.fl[6]=44; + Points->data.fl[7]=515; + + HomographyMatrix = cvCreateMat( 3, 3, CV_32FC1); + cvFindHomography(Points, ImgPoints, HomographyMatrix); + + for (int i=0;i<3;i++) + printf("%f %f %f \n",HomographyMatrix->data.fl[i*3],HomographyMatrix->data.fl[i*3+1],HomographyMatrix->data.fl[i*3+2]); + + cvWarpPerspective(image, med1, HomographyMatrix, CV_WARP_FILL_OUTLIERS, cvScalarAll(0)); + cvNamedWindow( "test", 1 ); + cvShowImage( "test", med1 ); + cvWaitKey(); + return 0; +} + diff --git a/HenocCameraDrv/HenocMouseDriver/HMouseDrv b/HenocCameraDrv/HenocMouseDriver/HMouseDrv new file mode 100755 index 0000000000000000000000000000000000000000..d5ac0c864bcc40148885657bdf51b4634996a83f GIT binary patch literal 98020 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 Y=CK;6X$WvLIMvVm=6Kox(+ +#include +#include +#include +#include +#include +#include +#include +#include + +//#include +#include +#include +#include + +#ifdef _EiC +#define WIN32 +#endif + +static CvMemStorage* storage = 0; +static CvHaarClassifierCascade* cascade = 0; + +void detect_and_draw( IplImage* image ); + +int main( int argc, char** argv ){ + gtk_init(&argc,&argv); + V4L2Capture *capture = 0; + + IplImage *frame, *frame_copy = 0; + const char* input_name; + + storage = cvCreateMemStorage(0); + + int isVideoDevice; + char* source = capture_source_chooser_run(&isVideoDevice); + if (source){ + if (isVideoDevice){ + capture = video_tuning_dialog_run(source); + } + free(source); + } + + cvNamedWindow( "result", 1 ); + + if( capture ){ + v4l2CaptureStartStreaming(capture,0,4); + for(;;){ + IplImage buffer; + frame = v4l2CaptureQueryIplImage(capture,&buffer); + if( !frame ) + break; + if( !frame_copy ) + frame_copy = cvCreateImage( cvSize(frame->width,frame->height), IPL_DEPTH_8U, frame->nChannels ); + if( frame->origin == IPL_ORIGIN_TL ) + cvCopy( frame, frame_copy, 0 ); + else + cvFlip( frame, frame_copy, 0 ); + detect_and_draw( frame_copy ); + if( cvWaitKey( 10 ) >= 0 ) + break; + } + cvReleaseImage( &frame_copy ); + v4l2CaptureRelease(&capture); + } + else{ + const char* filename = input_name ? input_name : (char*)"lena.jpg"; + IplImage* image = cvLoadImage( filename, 1 ); + + if( image ){ + detect_and_draw( image ); + cvWaitKey(0); + cvReleaseImage( &image ); + } + else{ + FILE* f = fopen( filename, "rt" ); + if( f ){ + char buf[1000+1]; + while( fgets( buf, 1000, f ) ){ + int len = (int)strlen(buf); + while( len > 0 && isspace(buf[len-1]) ) + len--; + buf[len] = '\0'; + image = cvLoadImage( buf, 1 ); + if( image ){ + detect_and_draw( image ); + cvWaitKey(0); + cvReleaseImage( &image ); + } + } + fclose(f); + } + } + } + + cvDestroyWindow("result"); + return 0; +} + +void detect_and_draw( IplImage* img ){ + static CvScalar colors[] = + { + {{0,0,255}}, + {{0,128,255}}, + {{0,255,255}}, + {{0,255,0}}, + {{255,128,0}}, + {{255,255,0}}, + {{255,0,0}}, + {{255,0,255}} + }; + + CvMat* HomographyMatrix = NULL; + HomographyMatrix = cvCreateMat( 3, 3, CV_32FC1); + HomographyMatrix->data.fl[0]=1.687285; + HomographyMatrix->data.fl[1]=0.379639; + HomographyMatrix->data.fl[2]=-269.754669; + HomographyMatrix->data.fl[3]=-0.048233; + HomographyMatrix->data.fl[4]=2.214323; + HomographyMatrix->data.fl[5]=-248.183975; + HomographyMatrix->data.fl[6]=-0.000076; + HomographyMatrix->data.fl[7]=0.000945; + HomographyMatrix->data.fl[8]=1.000000; + + IplImage* salida=cvCreateImage(cvGetSize(img),8,3); + cvWarpPerspective(img, salida, HomographyMatrix, CV_WARP_FILL_OUTLIERS, cvScalarAll(0)); + CvPoint center; + int i; + int j; + int radius; + int r=0; + int g=0; + int b=0; + int fao=0; + for(i=20; i < 640; i++) + for(j=0; j<800; j++){ + r = ((uchar*)(salida->imageData + salida->widthStep*i))[j*3+2]; + g = ((uchar*)(salida->imageData + salida->widthStep*i))[j*3+1]; + b = ((uchar*)(salida->imageData + salida->widthStep*i))[j*3]; + if( ( (r > 150) && (r < 230) ) && ( (b > 150) && (b < 230) ) && (g > 220) ){ + //if( ( (r > 150) && (r < 200) ) && ( (b > 150) && (b < 200) ) && (g > 220) ){ + //fao = 1; + //printf("lo encontre\n"); + //} + //if( ((r+g+b) > 680) && fao ){ + center.x = cvRound(j); + center.y = cvRound(i); + radius = cvRound( 20 ); + cvCircle( salida, center, radius, colors[1], 3, 8, 0 ); + + Display *display = XOpenDisplay(NULL); + int screen = DefaultScreen( display ); + XTestFakeMotionEvent( display, screen, center.x, center.y, 0 ); + XTestFakeButtonEvent( display, 1, True, 0 ); + XCloseDisplay( display ); + fao = 1; + goto pinta; + } + } + + pinta: + //cvShowImage( "result", img ); + if(!fao){ + Display *display = XOpenDisplay(NULL); + XTestFakeButtonEvent( display, 1, False, 0 ); + XCloseDisplay( display ); + } + + cvReleaseImage( &salida ); + cvReleaseMat( &HomographyMatrix ); +} diff --git a/HenocCameraDrv/HenocMouseDriver/Makefile b/HenocCameraDrv/HenocMouseDriver/Makefile new file mode 100755 index 0000000..2086d2f --- /dev/null +++ b/HenocCameraDrv/HenocMouseDriver/Makefile @@ -0,0 +1,3 @@ +all: + gcc -DHAVE_CONFIG_H -I. -I.. -I/usr/include/gtk-2.0 -I/usr/lib/gtk-2.0/include -I/usr/include/atk-1.0 -I/usr/include/cairo -I/usr/include/pango-1.0 -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/freetype2 -I/usr/include/libpng12 -I/usr/include/libglade-2.0 -I/usr/include/libxml2 -I/usr/include/opencv -O2 -o HenocMouseDriver.o HenocMouseDriver.c -c + gcc -O2 -export-dynamic -o HMouseDrv HenocMouseDriver.o -lXtst -lglade-2.0 -lgtk-x11-2.0 -lxml2 -lgdk-x11-2.0 -latk-1.0 -lgdk_pixbuf-2.0 -lm -lpangocairo-1.0 -lfontconfig -lXext -lXrender -lXinerama -lXi -lXrandr -lXcursor -lXfixes -lpango-1.0 -lcairo -lX11 -lgobject-2.0 -lgmodule-2.0 -ldl -lglib-2.0 -lcxcore -lcv -lhighgui -lcvaux -lml -L. -lrvtk-vc -Lopencv -lrvtk-opencv diff --git a/HenocCameraDrv/HenocMouseDriver/drv-v4l2.h b/HenocCameraDrv/HenocMouseDriver/drv-v4l2.h new file mode 100755 index 0000000..bbc0425 --- /dev/null +++ b/HenocCameraDrv/HenocMouseDriver/drv-v4l2.h @@ -0,0 +1,178 @@ +#ifndef V4L_H_ +#define V4L_H_ + +#include +#include +#include +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* defined(__cplusplus) */ + +/// Video capturing structure + +typedef struct { + int fd; + + /* General Information */ + + /// Device file (e.g /dev/video0) + char *location; + + /// Name of the device + char *name; + + /// The driver of the V4L device + char *driver; + + /// The data bus; + char *bus; + + /// The capabilities + unsigned int capabilities; + + /// no. of channels + int nChannels; + + /// An NULL terminated array of channel name + char **channel_list; + + /// no. of norms + int nNorms; + + /// An NULL terminated array of norm name + char **norm_list; + + /// An array of Video Stand for TV Norm. The size of this array is same as norm_list + v4l2_std_id* std_list; + + /// An NULL terminated array to hold supported fourcc code. + int *imageformat_list; + + /// no. of support buffer_types; + int buffer_types; + /// supported buffer types + int buffer_type[V4L2_BUF_TYPE_PRIVATE]; + + /* Run-time Information */ + + /// The current I/O moade = {V4L2_CAP_READWRITE(default) | V4L2_CAP_STREAMING} + int iomode; + + /// Burst mode + int burst_mode; + + /// The current input frame's pixel format in fourcc code (little endian) + int format; + + int channel; + + int norm; + + double fps; + + VidSize resolution; + + /// The buffer size of input frame. Assigned by v4l_dev_open/v4l_dev_get_image_format. + /** If it is set to 0 ,that means it is compressed format. Negative + * value indicate error (probably that the image format is not supported) + */ + int bufsize; + + int bytesperline; + + /// Turn on logging + int log; + + /* Buffers */ + + /// Buffer to reading frame + VidFrame *framesbuffer; + + /// no. of frames for buffer + int frames; + + /// Index to current frames buffer + int curr_frame_idx; + +} V4L2Capture; + + +///////////////////////// +/* Basic Operatiosn */ +///////////////////////// + +/// Initializes capturing video from V4L2 device +V4L2Capture* v4l2CaptureOpen(const char *location); + +/// Read a frame from device +VidFrame* v4l2CaptureQueryFrame(V4L2Capture*); + +/// Start streaming mode +int v4l2CaptureStartStreaming(V4L2Capture *capture,int burst,int nBuffer); + +/// Stop streaming mode +int v4l2CaputreStopStreaming(V4L2Capture *capture); + +/// Close and release the data allocated +void v4l2CaptureRelease(V4L2Capture**); + +void v4l2CaptureSetLog(V4L2Capture *capture,int state); + +////////////////////////////////////////////// +/* Query and set properties functions */ +////////////////////////////////////////////// + +const char* v4l2CaputreGetName(V4L2Capture *capture); +const char* v4l2CaputreGetLocation(V4L2Capture *capture); +const char* v4l2CaptureGetDriver(V4L2Capture *capture); + +/// Query the supported frame size for current image format +/** + * sizes: Output-variable to hold a newly created + * null-terminated array to hold the no. of support + * frame size. It must be released by user. + * + * Return: no. of available frame size. Negative value to indicate + * error. + */ +int v4l2CaptureQueryResolutionsList(V4L2Capture *,VidSize **sizes); + +/* Setters / Getters */ + +/// Return: Fourcc code of current image format +int v4l2CaptureGetImageFormat(V4L2Capture *capture); + +/// Set the image format and size +int v4l2CaptureSetImageFormat(V4L2Capture *capture,fourcc_t fourcc,VidSize *size); + +/// Auto-set the image format which could be converted to assigned output format. +int v4l2CaptureAutoSetImageFormat(V4L2Capture *capture,fourcc_t output); + +int v4l2CaptureGetResolution(V4L2Capture *capture,VidSize *size); +int v4l2CaptureSetResolution(V4L2Capture *capture,VidSize *size); + +double v4l2CaptureGetFPS(V4L2Capture *capture); +int v4l2CaptureSetFPS(V4L2Capture *capture,int fps); + +char** v4l2CaptureGetChannelsList(V4L2Capture *capture); +char** v4l2CaptureGetNormsList(V4L2Capture *capture); +int* v4l2CaptureGetImageFormatsList(V4L2Capture *capture); + +int v4l2CaptureSetChannel(V4L2Capture *,int index); +int v4l2CaptureGetChannel(V4L2Capture *); + +int v4l2CaptureSetNorm(V4L2Capture *,int index); +int v4l2CaptureGetNorm(V4L2Capture *); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* defined(__cplusplus) */ + + + +#endif /*V4L_H_*/ diff --git a/HenocCameraDrv/HenocMouseDriver/fourcc.h b/HenocCameraDrv/HenocMouseDriver/fourcc.h new file mode 100755 index 0000000..b650f6e --- /dev/null +++ b/HenocCameraDrv/HenocMouseDriver/fourcc.h @@ -0,0 +1,23 @@ +#ifndef FOURCC_H_ +#define FOURCC_H_ + +#ifdef __cplusplus +extern "C" { +#endif /* defined(__cplusplus) */ + +#define FOURCC(a,b,c,d) (((guint32)(a)<<0)|((guint32)(b)<<8)|((guint32)(c)<<16)|((guint32)(d)<<24)) + +typedef unsigned int fourcc_t; + +/// Return the name of a fourcc. +const char* vidFourccToString(fourcc_t code); + +/// Calculate the size of buffer required to hold a frame with specificed width, height and format. +int vidFourccCalcFrameSize(fourcc_t code,int width,int height); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* defined(__cplusplus) */ + + +#endif /*FOURCC_H_*/ diff --git a/HenocCameraDrv/HenocMouseDriver/frame.h b/HenocCameraDrv/HenocMouseDriver/frame.h new file mode 100755 index 0000000..1f5a040 --- /dev/null +++ b/HenocCameraDrv/HenocMouseDriver/frame.h @@ -0,0 +1,95 @@ +#ifndef FRAME_H_ +#define FRAME_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* defined(__cplusplus) */ + +/// Screen Size + +typedef struct { + /// The width + int width; + /// The height + int height; +} VidSize; + +/// Video Frame + +typedef struct { + /// The size of frame + VidSize size; + + /// The image type(fourcc) + fourcc_t format; + + int bytesperline; + + /// Excepted image size. + int imagesize; + + /// The size of data/buffer allocated to hold image. It should be larger than or equal to imagesize + int bufsize; + + /// Pointer to image data. + unsigned char *data; + + struct timeval timestamp; +} VidFrame; + +/// Allocate and initialize a V4L2Frame structure +VidFrame *vidFrameCreate(); + +/// Release a V4L2Frame. +void vidFrameRelease(VidFrame **frame); + +/// Get the width of a V4L2Frame +int vidFrameGetWidth(VidFrame *frame); + +/// Get the height of a V4L2Frame +int vidFrameGetHeight(VidFrame *frame) ; + +/// Get the image size of a V4L2Frame +int vidFrameGetImageSize(VidFrame *frame); + +/// Get the pointer to image data +unsigned char* vidFrameGetImageData(VidFrame *frame); + +/// Resize the buffer length of image data. +int vidFrameResizeBuffer(VidFrame *frame,int length); + +/* Image Format Convertor */ + +typedef int (*v4l2ConvFunc) (VidFrame *src,VidFrame *dest); + +/// Image format converter + +typedef struct { + /// Name of the converter + char *name; + /// The input image format(fourcc) + fourcc_t input; + /// The output image format(fourcc)) + fourcc_t output; + + /// The callback function to handle the convertion + v4l2ConvFunc convert; +} VidConv; + +/// Find a converter to convert image format from input to output + +VidConv* vidConvFind(fourcc_t input,fourcc_t output); + +/// Execute the image converter. + +int vidConvProcess(VidConv *conv,VidFrame *src,VidFrame *dest); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* defined(__cplusplus) */ + + +#endif /*FRAME_H_*/ diff --git a/HenocCameraDrv/HenocMouseDriver/opencv/.deps/cvutils.Po b/HenocCameraDrv/HenocMouseDriver/opencv/.deps/cvutils.Po new file mode 100755 index 0000000..3423d4a --- /dev/null +++ b/HenocCameraDrv/HenocMouseDriver/opencv/.deps/cvutils.Po @@ -0,0 +1,147 @@ +cvutils.o cvutils.o: cvutils.c /usr/include/sys/time.h \ + /usr/include/features.h /usr/include/sys/cdefs.h \ + /usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \ + /usr/include/gnu/stubs-32.h /usr/include/bits/types.h \ + /usr/lib/gcc/i486-linux-gnu/4.1.2/include/stddef.h \ + /usr/include/bits/typesizes.h /usr/include/time.h \ + /usr/include/bits/time.h /usr/include/sys/select.h \ + /usr/include/bits/select.h /usr/include/bits/sigset.h cvutils.h \ + ../../../src/rvtk/v4l2.h ../../../src/rvtk/drv-v4l2.h \ + /usr/include/linux/ioctl.h /usr/include/asm/ioctl.h \ + /usr/include/asm-i386/ioctl.h /usr/include/asm-generic/ioctl.h \ + /usr/include/linux/videodev.h /usr/include/linux/videodev2.h \ + /usr/include/linux/types.h /usr/include/linux/posix_types.h \ + /usr/include/linux/stddef.h \ + /usr/lib/gcc/i486-linux-gnu/4.1.2/include/asm/posix_types.h \ + /usr/include/asm/posix_types.h /usr/include/asm-i386/posix_types.h \ + /usr/include/asm/types.h /usr/include/asm-i386/types.h \ + ../../../src/rvtk/fourcc.h ../../../src/rvtk/frame.h \ + /usr/include/opencv/cv.h /usr/include/opencv/cxcore.h \ + /usr/include/opencv/cxtypes.h /usr/include/assert.h \ + /usr/include/stdlib.h /usr/include/sys/types.h /usr/include/endian.h \ + /usr/include/bits/endian.h /usr/include/sys/sysmacros.h \ + /usr/include/bits/pthreadtypes.h /usr/include/alloca.h \ + /usr/include/string.h /usr/include/bits/string.h \ + /usr/include/bits/string2.h \ + /usr/lib/gcc/i486-linux-gnu/4.1.2/include/float.h /usr/include/math.h \ + /usr/include/bits/huge_val.h /usr/include/bits/mathdef.h \ + /usr/include/bits/mathcalls.h /usr/include/bits/mathinline.h \ + /usr/include/opencv/cxerror.h /usr/include/opencv/cvver.h \ + /usr/include/opencv/cvtypes.h /usr/include/opencv/cvcompat.h \ + /usr/include/opencv/highgui.h + +/usr/include/sys/time.h: + +/usr/include/features.h: + +/usr/include/sys/cdefs.h: + +/usr/include/bits/wordsize.h: + +/usr/include/gnu/stubs.h: + +/usr/include/gnu/stubs-32.h: + +/usr/include/bits/types.h: + +/usr/lib/gcc/i486-linux-gnu/4.1.2/include/stddef.h: + +/usr/include/bits/typesizes.h: + +/usr/include/time.h: + +/usr/include/bits/time.h: + +/usr/include/sys/select.h: + +/usr/include/bits/select.h: + +/usr/include/bits/sigset.h: + +cvutils.h: + +../../../src/rvtk/v4l2.h: + +../../../src/rvtk/drv-v4l2.h: + +/usr/include/linux/ioctl.h: + +/usr/include/asm/ioctl.h: + +/usr/include/asm-i386/ioctl.h: + +/usr/include/asm-generic/ioctl.h: + +/usr/include/linux/videodev.h: + +/usr/include/linux/videodev2.h: + +/usr/include/linux/types.h: + +/usr/include/linux/posix_types.h: + +/usr/include/linux/stddef.h: + +/usr/lib/gcc/i486-linux-gnu/4.1.2/include/asm/posix_types.h: + +/usr/include/asm/posix_types.h: + +/usr/include/asm-i386/posix_types.h: + +/usr/include/asm/types.h: + +/usr/include/asm-i386/types.h: + +../../../src/rvtk/fourcc.h: + +../../../src/rvtk/frame.h: + +/usr/include/opencv/cv.h: + +/usr/include/opencv/cxcore.h: + +/usr/include/opencv/cxtypes.h: + +/usr/include/assert.h: + +/usr/include/stdlib.h: + +/usr/include/sys/types.h: + +/usr/include/endian.h: + +/usr/include/bits/endian.h: + +/usr/include/sys/sysmacros.h: + +/usr/include/bits/pthreadtypes.h: + +/usr/include/alloca.h: + +/usr/include/string.h: + +/usr/include/bits/string.h: + +/usr/include/bits/string2.h: + +/usr/lib/gcc/i486-linux-gnu/4.1.2/include/float.h: + +/usr/include/math.h: + +/usr/include/bits/huge_val.h: + +/usr/include/bits/mathdef.h: + +/usr/include/bits/mathcalls.h: + +/usr/include/bits/mathinline.h: + +/usr/include/opencv/cxerror.h: + +/usr/include/opencv/cvver.h: + +/usr/include/opencv/cvtypes.h: + +/usr/include/opencv/cvcompat.h: + +/usr/include/opencv/highgui.h: diff --git a/HenocCameraDrv/HenocMouseDriver/opencv/.deps/ui.Po b/HenocCameraDrv/HenocMouseDriver/opencv/.deps/ui.Po new file mode 100755 index 0000000..e9d753f --- /dev/null +++ b/HenocCameraDrv/HenocMouseDriver/opencv/.deps/ui.Po @@ -0,0 +1,1216 @@ +ui.o ui.o: ui.c /usr/include/stdio.h /usr/include/features.h \ + /usr/include/sys/cdefs.h /usr/include/bits/wordsize.h \ + /usr/include/gnu/stubs.h /usr/include/gnu/stubs-32.h \ + /usr/lib/gcc/i486-linux-gnu/4.1.2/include/stddef.h \ + /usr/include/bits/types.h /usr/include/bits/typesizes.h \ + /usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \ + /usr/include/bits/wchar.h /usr/include/gconv.h \ + /usr/lib/gcc/i486-linux-gnu/4.1.2/include/stdarg.h \ + /usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \ + /usr/include/bits/stdio.h /usr/include/stdlib.h \ + /usr/include/sys/types.h /usr/include/time.h /usr/include/endian.h \ + /usr/include/bits/endian.h /usr/include/sys/select.h \ + /usr/include/bits/select.h /usr/include/bits/sigset.h \ + /usr/include/bits/time.h /usr/include/sys/sysmacros.h \ + /usr/include/bits/pthreadtypes.h /usr/include/alloca.h \ + /usr/include/math.h /usr/include/bits/huge_val.h \ + /usr/include/bits/mathdef.h /usr/include/bits/mathcalls.h \ + /usr/include/bits/mathinline.h ui.h /usr/include/gtk-2.0/gtk/gtk.h \ + /usr/include/gtk-2.0/gdk/gdk.h /usr/include/gtk-2.0/gdk/gdkcairo.h \ + /usr/include/gtk-2.0/gdk/gdkcolor.h /usr/include/cairo/cairo.h \ + /usr/include/cairo/cairo-features.h \ + /usr/include/cairo/cairo-deprecated.h \ + /usr/include/gtk-2.0/gdk/gdktypes.h /usr/include/glib-2.0/glib.h \ + /usr/include/glib-2.0/glib/galloca.h \ + /usr/include/glib-2.0/glib/gtypes.h \ + /usr/lib/glib-2.0/include/glibconfig.h \ + /usr/include/glib-2.0/glib/gmacros.h \ + /usr/lib/gcc/i486-linux-gnu/4.1.2/include/limits.h \ + /usr/lib/gcc/i486-linux-gnu/4.1.2/include/syslimits.h \ + /usr/include/limits.h /usr/include/bits/posix1_lim.h \ + /usr/include/bits/local_lim.h /usr/include/linux/limits.h \ + /usr/include/bits/posix2_lim.h \ + /usr/lib/gcc/i486-linux-gnu/4.1.2/include/float.h \ + /usr/include/glib-2.0/glib/garray.h \ + /usr/include/glib-2.0/glib/gasyncqueue.h \ + /usr/include/glib-2.0/glib/gthread.h \ + /usr/include/glib-2.0/glib/gerror.h /usr/include/glib-2.0/glib/gquark.h \ + /usr/include/glib-2.0/glib/gatomic.h \ + /usr/include/glib-2.0/glib/gbacktrace.h \ + /usr/include/glib-2.0/glib/gbase64.h \ + /usr/include/glib-2.0/glib/gbookmarkfile.h \ + /usr/include/glib-2.0/glib/gcache.h /usr/include/glib-2.0/glib/glist.h \ + /usr/include/glib-2.0/glib/gmem.h /usr/include/glib-2.0/glib/gslice.h \ + /usr/include/glib-2.0/glib/gcompletion.h \ + /usr/include/glib-2.0/glib/gconvert.h \ + /usr/include/glib-2.0/glib/gdataset.h \ + /usr/include/glib-2.0/glib/gdate.h /usr/include/glib-2.0/glib/gdir.h \ + /usr/include/glib-2.0/glib/gfileutils.h \ + /usr/include/glib-2.0/glib/ghash.h /usr/include/glib-2.0/glib/ghook.h \ + /usr/include/glib-2.0/glib/giochannel.h \ + /usr/include/glib-2.0/glib/gmain.h /usr/include/glib-2.0/glib/gslist.h \ + /usr/include/glib-2.0/glib/gstring.h \ + /usr/include/glib-2.0/glib/gunicode.h \ + /usr/include/glib-2.0/glib/gutils.h \ + /usr/include/glib-2.0/glib/gkeyfile.h \ + /usr/include/glib-2.0/glib/gmappedfile.h \ + /usr/include/glib-2.0/glib/gmarkup.h \ + /usr/include/glib-2.0/glib/gmessages.h \ + /usr/include/glib-2.0/glib/gnode.h /usr/include/glib-2.0/glib/goption.h \ + /usr/include/glib-2.0/glib/gpattern.h \ + /usr/include/glib-2.0/glib/gprimes.h \ + /usr/include/glib-2.0/glib/gqsort.h /usr/include/glib-2.0/glib/gqueue.h \ + /usr/include/glib-2.0/glib/grand.h /usr/include/glib-2.0/glib/grel.h \ + /usr/include/glib-2.0/glib/gscanner.h \ + /usr/include/glib-2.0/glib/gshell.h /usr/include/glib-2.0/glib/gspawn.h \ + /usr/include/glib-2.0/glib/gstrfuncs.h \ + /usr/include/glib-2.0/glib/gthreadpool.h \ + /usr/include/glib-2.0/glib/gtimer.h /usr/include/glib-2.0/glib/gtree.h \ + /usr/include/pango-1.0/pango/pango.h \ + /usr/include/pango-1.0/pango/pango-attributes.h \ + /usr/include/pango-1.0/pango/pango-font.h \ + /usr/include/pango-1.0/pango/pango-coverage.h \ + /usr/include/pango-1.0/pango/pango-types.h \ + /usr/include/glib-2.0/glib-object.h \ + /usr/include/glib-2.0/gobject/gboxed.h \ + /usr/include/glib-2.0/gobject/gtype.h \ + /usr/include/glib-2.0/gobject/genums.h \ + /usr/include/glib-2.0/gobject/gobject.h \ + /usr/include/glib-2.0/gobject/gvalue.h \ + /usr/include/glib-2.0/gobject/gparam.h \ + /usr/include/glib-2.0/gobject/gclosure.h \ + /usr/include/glib-2.0/gobject/gsignal.h \ + /usr/include/glib-2.0/gobject/gmarshal.h \ + /usr/include/glib-2.0/gobject/gparamspecs.h \ + /usr/include/glib-2.0/gobject/gsourceclosure.h \ + /usr/include/glib-2.0/gobject/gtypemodule.h \ + /usr/include/glib-2.0/gobject/gtypeplugin.h \ + /usr/include/glib-2.0/gobject/gvaluearray.h \ + /usr/include/glib-2.0/gobject/gvaluetypes.h \ + /usr/include/pango-1.0/pango/pango-matrix.h \ + /usr/include/pango-1.0/pango/pango-script.h \ + /usr/include/pango-1.0/pango/pango-gravity.h \ + /usr/include/pango-1.0/pango/pango-break.h \ + /usr/include/pango-1.0/pango/pango-item.h \ + /usr/include/pango-1.0/pango/pango-context.h \ + /usr/include/pango-1.0/pango/pango-fontmap.h \ + /usr/include/pango-1.0/pango/pango-fontset.h \ + /usr/include/pango-1.0/pango/pango-engine.h \ + /usr/include/pango-1.0/pango/pango-glyph.h \ + /usr/include/pango-1.0/pango/pango-enum-types.h \ + /usr/include/pango-1.0/pango/pango-features.h \ + /usr/include/pango-1.0/pango/pango-glyph-item.h \ + /usr/include/pango-1.0/pango/pango-layout.h \ + /usr/include/pango-1.0/pango/pango-tabs.h \ + /usr/include/pango-1.0/pango/pango-renderer.h \ + /usr/include/pango-1.0/pango/pango-utils.h \ + /usr/lib/gtk-2.0/include/gdkconfig.h \ + /usr/include/gtk-2.0/gdk/gdkpixbuf.h /usr/include/gtk-2.0/gdk/gdkrgb.h \ + /usr/include/gtk-2.0/gdk-pixbuf/gdk-pixbuf.h \ + /usr/include/gtk-2.0/gdk-pixbuf/gdk-pixbuf-features.h \ + /usr/include/gtk-2.0/gdk-pixbuf/gdk-pixbuf-core.h \ + /usr/include/gtk-2.0/gdk-pixbuf/gdk-pixbuf-transform.h \ + /usr/include/gtk-2.0/gdk-pixbuf/gdk-pixbuf-animation.h \ + /usr/include/gtk-2.0/gdk-pixbuf/gdk-pixbuf-simple-anim.h \ + /usr/include/gtk-2.0/gdk-pixbuf/gdk-pixbuf-io.h \ + /usr/include/glib-2.0/gmodule.h \ + /usr/include/gtk-2.0/gdk-pixbuf/gdk-pixbuf-loader.h \ + /usr/include/gtk-2.0/gdk-pixbuf/gdk-pixbuf-enum-types.h \ + /usr/include/pango-1.0/pango/pangocairo.h \ + /usr/include/gtk-2.0/gdk/gdkcursor.h \ + /usr/include/gtk-2.0/gdk/gdkdisplay.h \ + /usr/include/gtk-2.0/gdk/gdkevents.h /usr/include/gtk-2.0/gdk/gdkdnd.h \ + /usr/include/gtk-2.0/gdk/gdkinput.h \ + /usr/include/gtk-2.0/gdk/gdkdrawable.h /usr/include/gtk-2.0/gdk/gdkgc.h \ + /usr/include/gtk-2.0/gdk/gdkenumtypes.h \ + /usr/include/gtk-2.0/gdk/gdkfont.h /usr/include/gtk-2.0/gdk/gdkimage.h \ + /usr/include/gtk-2.0/gdk/gdkkeys.h \ + /usr/include/gtk-2.0/gdk/gdkdisplaymanager.h \ + /usr/include/gtk-2.0/gdk/gdkpango.h \ + /usr/include/gtk-2.0/gdk/gdkpixmap.h \ + /usr/include/gtk-2.0/gdk/gdkproperty.h \ + /usr/include/gtk-2.0/gdk/gdkregion.h \ + /usr/include/gtk-2.0/gdk/gdkscreen.h \ + /usr/include/gtk-2.0/gdk/gdkselection.h \ + /usr/include/gtk-2.0/gdk/gdkspawn.h \ + /usr/include/gtk-2.0/gdk/gdkvisual.h \ + /usr/include/gtk-2.0/gdk/gdkwindow.h \ + /usr/include/gtk-2.0/gtk/gtkaboutdialog.h \ + /usr/include/gtk-2.0/gtk/gtkdialog.h \ + /usr/include/gtk-2.0/gtk/gtkwindow.h \ + /usr/include/gtk-2.0/gtk/gtkaccelgroup.h \ + /usr/include/gtk-2.0/gtk/gtkenums.h /usr/include/gtk-2.0/gtk/gtkbin.h \ + /usr/include/gtk-2.0/gtk/gtkcontainer.h \ + /usr/include/gtk-2.0/gtk/gtkwidget.h \ + /usr/include/gtk-2.0/gtk/gtkobject.h \ + /usr/include/gtk-2.0/gtk/gtktypeutils.h \ + /usr/include/gtk-2.0/gtk/gtktypebuiltins.h \ + /usr/include/gtk-2.0/gtk/gtkdebug.h \ + /usr/include/gtk-2.0/gtk/gtkadjustment.h \ + /usr/include/gtk-2.0/gtk/gtkstyle.h \ + /usr/include/gtk-2.0/gtk/gtksettings.h /usr/include/gtk-2.0/gtk/gtkrc.h \ + /usr/include/atk-1.0/atk/atkobject.h \ + /usr/include/atk-1.0/atk/atkstate.h \ + /usr/include/atk-1.0/atk/atkrelationtype.h \ + /usr/include/gtk-2.0/gtk/gtkaccellabel.h \ + /usr/include/gtk-2.0/gtk/gtklabel.h /usr/include/gtk-2.0/gtk/gtkmisc.h \ + /usr/include/gtk-2.0/gtk/gtkmenu.h \ + /usr/include/gtk-2.0/gtk/gtkmenushell.h \ + /usr/include/gtk-2.0/gtk/gtkaccelmap.h \ + /usr/include/gtk-2.0/gtk/gtkaccessible.h /usr/include/atk-1.0/atk/atk.h \ + /usr/include/atk-1.0/atk/atkaction.h \ + /usr/include/atk-1.0/atk/atkcomponent.h \ + /usr/include/atk-1.0/atk/atkutil.h \ + /usr/include/atk-1.0/atk/atkdocument.h \ + /usr/include/atk-1.0/atk/atkeditabletext.h \ + /usr/include/atk-1.0/atk/atktext.h \ + /usr/include/atk-1.0/atk/atkgobjectaccessible.h \ + /usr/include/atk-1.0/atk/atkhyperlink.h \ + /usr/include/atk-1.0/atk/atkhyperlinkimpl.h \ + /usr/include/atk-1.0/atk/atkhypertext.h \ + /usr/include/atk-1.0/atk/atkimage.h \ + /usr/include/atk-1.0/atk/atknoopobject.h \ + /usr/include/atk-1.0/atk/atknoopobjectfactory.h \ + /usr/include/atk-1.0/atk/atkobjectfactory.h \ + /usr/include/atk-1.0/atk/atkregistry.h \ + /usr/include/atk-1.0/atk/atkobjectfactory.h \ + /usr/include/atk-1.0/atk/atkrelation.h \ + /usr/include/atk-1.0/atk/atkrelationset.h \ + /usr/include/atk-1.0/atk/atkselection.h \ + /usr/include/atk-1.0/atk/atkstateset.h \ + /usr/include/atk-1.0/atk/atkstreamablecontent.h \ + /usr/include/atk-1.0/atk/atktable.h /usr/include/atk-1.0/atk/atkmisc.h \ + /usr/include/atk-1.0/atk/atkvalue.h \ + /usr/include/gtk-2.0/gtk/gtkaction.h \ + /usr/include/gtk-2.0/gtk/gtkactiongroup.h \ + /usr/include/gtk-2.0/gtk/gtkitemfactory.h \ + /usr/include/gtk-2.0/gtk/gtkalignment.h \ + /usr/include/gtk-2.0/gtk/gtkarrow.h \ + /usr/include/gtk-2.0/gtk/gtkaspectframe.h \ + /usr/include/gtk-2.0/gtk/gtkframe.h \ + /usr/include/gtk-2.0/gtk/gtkassistant.h \ + /usr/include/gtk-2.0/gtk/gtkbbox.h /usr/include/gtk-2.0/gtk/gtkbox.h \ + /usr/include/gtk-2.0/gtk/gtkbindings.h \ + /usr/include/gtk-2.0/gtk/gtkbutton.h \ + /usr/include/gtk-2.0/gtk/gtkimage.h \ + /usr/include/gtk-2.0/gtk/gtkcalendar.h \ + /usr/include/gtk-2.0/gtk/gtksignal.h \ + /usr/include/gtk-2.0/gtk/gtkmarshal.h \ + /usr/include/gtk-2.0/gtk/gtkcelllayout.h \ + /usr/include/gtk-2.0/gtk/gtkcellrenderer.h \ + /usr/include/gtk-2.0/gtk/gtkcelleditable.h \ + /usr/include/gtk-2.0/gtk/gtktreeviewcolumn.h \ + /usr/include/gtk-2.0/gtk/gtktreemodel.h \ + /usr/include/gtk-2.0/gtk/gtktreesortable.h \ + /usr/include/gtk-2.0/gtk/gtkcellrendereraccel.h \ + /usr/include/gtk-2.0/gtk/gtkcellrenderertext.h \ + /usr/include/gtk-2.0/gtk/gtkcellrenderercombo.h \ + /usr/include/gtk-2.0/gtk/gtkcellrenderertext.h \ + /usr/include/gtk-2.0/gtk/gtkcellrendererpixbuf.h \ + /usr/include/gtk-2.0/gtk/gtkcellrendererprogress.h \ + /usr/include/gtk-2.0/gtk/gtkcellrendererspin.h \ + /usr/include/gtk-2.0/gtk/gtkcellrenderertoggle.h \ + /usr/include/gtk-2.0/gtk/gtkcellview.h \ + /usr/include/gtk-2.0/gtk/gtkcheckbutton.h \ + /usr/include/gtk-2.0/gtk/gtktogglebutton.h \ + /usr/include/gtk-2.0/gtk/gtkcheckmenuitem.h \ + /usr/include/gtk-2.0/gtk/gtkmenuitem.h \ + /usr/include/gtk-2.0/gtk/gtkitem.h \ + /usr/include/gtk-2.0/gtk/gtkclipboard.h \ + /usr/include/gtk-2.0/gtk/gtkselection.h \ + /usr/include/gtk-2.0/gtk/gtktextiter.h \ + /usr/include/gtk-2.0/gtk/gtktexttag.h \ + /usr/include/gtk-2.0/gtk/gtktextchild.h \ + /usr/include/gtk-2.0/gtk/gtkclist.h \ + /usr/include/gtk-2.0/gtk/gtkhscrollbar.h \ + /usr/include/gtk-2.0/gtk/gtkscrollbar.h \ + /usr/include/gtk-2.0/gtk/gtkrange.h \ + /usr/include/gtk-2.0/gtk/gtkvscrollbar.h \ + /usr/include/gtk-2.0/gtk/gtkcolorbutton.h \ + /usr/include/gtk-2.0/gtk/gtkcolorsel.h \ + /usr/include/gtk-2.0/gtk/gtkvbox.h \ + /usr/include/gtk-2.0/gtk/gtkcolorseldialog.h \ + /usr/include/gtk-2.0/gtk/gtkcombo.h /usr/include/gtk-2.0/gtk/gtkhbox.h \ + /usr/include/gtk-2.0/gtk/gtkcombobox.h \ + /usr/include/gtk-2.0/gtk/gtktreeview.h \ + /usr/include/gtk-2.0/gtk/gtkdnd.h /usr/include/gtk-2.0/gtk/gtkentry.h \ + /usr/include/gtk-2.0/gtk/gtkeditable.h \ + /usr/include/gtk-2.0/gtk/gtkimcontext.h \ + /usr/include/gtk-2.0/gtk/gtkentrycompletion.h \ + /usr/include/gtk-2.0/gtk/gtkliststore.h \ + /usr/include/gtk-2.0/gtk/gtktreemodelfilter.h \ + /usr/include/gtk-2.0/gtk/gtkcomboboxentry.h \ + /usr/include/gtk-2.0/gtk/gtkctree.h /usr/include/gtk-2.0/gtk/gtkcurve.h \ + /usr/include/gtk-2.0/gtk/gtkdrawingarea.h \ + /usr/include/gtk-2.0/gtk/gtkeventbox.h \ + /usr/include/gtk-2.0/gtk/gtkexpander.h \ + /usr/include/gtk-2.0/gtk/gtkfilesel.h \ + /usr/include/gtk-2.0/gtk/gtkfixed.h \ + /usr/include/gtk-2.0/gtk/gtkfilechooserbutton.h \ + /usr/include/gtk-2.0/gtk/gtkfilechooser.h \ + /usr/include/gtk-2.0/gtk/gtkfilefilter.h \ + /usr/include/gtk-2.0/gtk/gtkfilechooserdialog.h \ + /usr/include/gtk-2.0/gtk/gtkfilechooser.h \ + /usr/include/gtk-2.0/gtk/gtkfilechooserwidget.h \ + /usr/include/gtk-2.0/gtk/gtkfontbutton.h \ + /usr/include/gtk-2.0/gtk/gtkfontsel.h \ + /usr/include/gtk-2.0/gtk/gtkgamma.h /usr/include/gtk-2.0/gtk/gtkgc.h \ + /usr/include/gtk-2.0/gtk/gtkhandlebox.h \ + /usr/include/gtk-2.0/gtk/gtkhbbox.h \ + /usr/include/gtk-2.0/gtk/gtkhpaned.h \ + /usr/include/gtk-2.0/gtk/gtkpaned.h \ + /usr/include/gtk-2.0/gtk/gtkhruler.h \ + /usr/include/gtk-2.0/gtk/gtkruler.h \ + /usr/include/gtk-2.0/gtk/gtkhscale.h \ + /usr/include/gtk-2.0/gtk/gtkscale.h \ + /usr/include/gtk-2.0/gtk/gtkhseparator.h \ + /usr/include/gtk-2.0/gtk/gtkseparator.h \ + /usr/include/gtk-2.0/gtk/gtkiconfactory.h \ + /usr/include/gtk-2.0/gtk/gtkicontheme.h \ + /usr/include/gtk-2.0/gtk/gtkiconview.h \ + /usr/include/gtk-2.0/gtk/gtkimagemenuitem.h \ + /usr/include/gtk-2.0/gtk/gtkimcontextsimple.h \ + /usr/include/gtk-2.0/gtk/gtkimmulticontext.h \ + /usr/include/gtk-2.0/gtk/gtkinputdialog.h \ + /usr/include/gtk-2.0/gtk/gtkinvisible.h \ + /usr/include/gtk-2.0/gtk/gtklayout.h \ + /usr/include/gtk-2.0/gtk/gtklinkbutton.h \ + /usr/include/gtk-2.0/gtk/gtklist.h \ + /usr/include/gtk-2.0/gtk/gtklistitem.h \ + /usr/include/gtk-2.0/gtk/gtkmain.h \ + /usr/include/gtk-2.0/gtk/gtkmenubar.h \ + /usr/include/gtk-2.0/gtk/gtkmenutoolbutton.h \ + /usr/include/gtk-2.0/gtk/gtkmenu.h \ + /usr/include/gtk-2.0/gtk/gtktoolbutton.h \ + /usr/include/gtk-2.0/gtk/gtktoolitem.h \ + /usr/include/gtk-2.0/gtk/gtktooltips.h \ + /usr/include/gtk-2.0/gtk/gtkmessagedialog.h \ + /usr/include/gtk-2.0/gtk/gtkmodules.h \ + /usr/include/gtk-2.0/gtk/gtknotebook.h \ + /usr/include/gtk-2.0/gtk/gtkoldeditable.h \ + /usr/include/gtk-2.0/gtk/gtkoptionmenu.h \ + /usr/include/gtk-2.0/gtk/gtkpixmap.h /usr/include/gtk-2.0/gtk/gtkplug.h \ + /usr/include/gtk-2.0/gtk/gtksocket.h \ + /usr/include/gtk-2.0/gtk/gtkpreview.h \ + /usr/include/gtk-2.0/gtk/gtkprintoperation.h \ + /usr/include/gtk-2.0/gtk/gtkmain.h /usr/include/gtk-2.0/gtk/gtkenums.h \ + /usr/include/gtk-2.0/gtk/gtkwindow.h \ + /usr/include/gtk-2.0/gtk/gtkpagesetup.h \ + /usr/include/gtk-2.0/gtk/gtkpapersize.h \ + /usr/include/gtk-2.0/gtk/gtkprintsettings.h \ + /usr/include/gtk-2.0/gtk/gtkprintcontext.h \ + /usr/include/gtk-2.0/gtk/gtkprintoperationpreview.h \ + /usr/include/gtk-2.0/gtk/gtkprogress.h \ + /usr/include/gtk-2.0/gtk/gtkprogressbar.h \ + /usr/include/gtk-2.0/gtk/gtkradioaction.h \ + /usr/include/gtk-2.0/gtk/gtktoggleaction.h \ + /usr/include/gtk-2.0/gtk/gtkradiobutton.h \ + /usr/include/gtk-2.0/gtk/gtkradiomenuitem.h \ + /usr/include/gtk-2.0/gtk/gtkradiotoolbutton.h \ + /usr/include/gtk-2.0/gtk/gtktoggletoolbutton.h \ + /usr/include/gtk-2.0/gtk/gtktoolbutton.h \ + /usr/include/gtk-2.0/gtk/gtkrecentchooser.h \ + /usr/include/gtk-2.0/gtk/gtkrecentmanager.h \ + /usr/include/gtk-2.0/gtk/gtkrecentfilter.h \ + /usr/include/gtk-2.0/gtk/gtkrecentchooserdialog.h \ + /usr/include/gtk-2.0/gtk/gtkrecentchooser.h \ + /usr/include/gtk-2.0/gtk/gtkrecentchoosermenu.h \ + /usr/include/gtk-2.0/gtk/gtkrecentchooserwidget.h \ + /usr/include/gtk-2.0/gtk/gtkrecentfilter.h \ + /usr/include/gtk-2.0/gtk/gtkrecentmanager.h \ + /usr/include/gtk-2.0/gtk/gtkscrolledwindow.h \ + /usr/include/gtk-2.0/gtk/gtkviewport.h \ + /usr/include/gtk-2.0/gtk/gtkseparatormenuitem.h \ + /usr/include/gtk-2.0/gtk/gtkseparatortoolitem.h \ + /usr/include/gtk-2.0/gtk/gtksizegroup.h \ + /usr/include/gtk-2.0/gtk/gtkspinbutton.h \ + /usr/include/gtk-2.0/gtk/gtkstatusbar.h \ + /usr/include/gtk-2.0/gtk/gtkstatusicon.h \ + /usr/include/gtk-2.0/gtk/gtkstock.h /usr/include/gtk-2.0/gtk/gtktable.h \ + /usr/include/gtk-2.0/gtk/gtktearoffmenuitem.h \ + /usr/include/gtk-2.0/gtk/gtktext.h \ + /usr/include/gtk-2.0/gtk/gtktextbuffer.h \ + /usr/include/gtk-2.0/gtk/gtktexttagtable.h \ + /usr/include/gtk-2.0/gtk/gtktextmark.h \ + /usr/include/gtk-2.0/gtk/gtktextbufferrichtext.h \ + /usr/include/gtk-2.0/gtk/gtktextview.h \ + /usr/include/gtk-2.0/gtk/gtktipsquery.h \ + /usr/include/gtk-2.0/gtk/gtktoggletoolbutton.h \ + /usr/include/gtk-2.0/gtk/gtktoolbar.h \ + /usr/include/gtk-2.0/gtk/gtktoolbutton.h \ + /usr/include/gtk-2.0/gtk/gtktoolitem.h \ + /usr/include/gtk-2.0/gtk/gtktree.h \ + /usr/include/gtk-2.0/gtk/gtktreednd.h \ + /usr/include/gtk-2.0/gtk/gtktreeitem.h \ + /usr/include/gtk-2.0/gtk/gtktreemodelsort.h \ + /usr/include/gtk-2.0/gtk/gtktreeselection.h \ + /usr/include/gtk-2.0/gtk/gtktreestore.h \ + /usr/include/gtk-2.0/gtk/gtkuimanager.h \ + /usr/include/gtk-2.0/gtk/gtkvbbox.h \ + /usr/include/gtk-2.0/gtk/gtkversion.h \ + /usr/include/gtk-2.0/gtk/gtkvpaned.h \ + /usr/include/gtk-2.0/gtk/gtkvruler.h \ + /usr/include/gtk-2.0/gtk/gtkvscale.h \ + /usr/include/gtk-2.0/gtk/gtkvseparator.h ../../../src/rvtk/v4l2.h \ + ../../../src/rvtk/drv-v4l2.h /usr/include/linux/ioctl.h \ + /usr/include/asm/ioctl.h /usr/include/asm-i386/ioctl.h \ + /usr/include/asm-generic/ioctl.h /usr/include/linux/videodev.h \ + /usr/include/linux/videodev2.h /usr/include/linux/types.h \ + /usr/include/linux/posix_types.h /usr/include/linux/stddef.h \ + /usr/lib/gcc/i486-linux-gnu/4.1.2/include/asm/posix_types.h \ + /usr/include/asm/posix_types.h /usr/include/asm-i386/posix_types.h \ + /usr/include/asm/types.h /usr/include/asm-i386/types.h \ + ../../../src/rvtk/fourcc.h ../../../src/rvtk/frame.h \ + /usr/include/sys/time.h camera-photo.h + +/usr/include/stdio.h: + +/usr/include/features.h: + +/usr/include/sys/cdefs.h: + +/usr/include/bits/wordsize.h: + +/usr/include/gnu/stubs.h: + +/usr/include/gnu/stubs-32.h: + +/usr/lib/gcc/i486-linux-gnu/4.1.2/include/stddef.h: + +/usr/include/bits/types.h: + +/usr/include/bits/typesizes.h: + +/usr/include/libio.h: + +/usr/include/_G_config.h: + +/usr/include/wchar.h: + +/usr/include/bits/wchar.h: + +/usr/include/gconv.h: + +/usr/lib/gcc/i486-linux-gnu/4.1.2/include/stdarg.h: + +/usr/include/bits/stdio_lim.h: + +/usr/include/bits/sys_errlist.h: + +/usr/include/bits/stdio.h: + +/usr/include/stdlib.h: + +/usr/include/sys/types.h: + +/usr/include/time.h: + +/usr/include/endian.h: + +/usr/include/bits/endian.h: + +/usr/include/sys/select.h: + +/usr/include/bits/select.h: + +/usr/include/bits/sigset.h: + +/usr/include/bits/time.h: + +/usr/include/sys/sysmacros.h: + +/usr/include/bits/pthreadtypes.h: + +/usr/include/alloca.h: + +/usr/include/math.h: + +/usr/include/bits/huge_val.h: + +/usr/include/bits/mathdef.h: + +/usr/include/bits/mathcalls.h: + +/usr/include/bits/mathinline.h: + +ui.h: + +/usr/include/gtk-2.0/gtk/gtk.h: + +/usr/include/gtk-2.0/gdk/gdk.h: + +/usr/include/gtk-2.0/gdk/gdkcairo.h: + +/usr/include/gtk-2.0/gdk/gdkcolor.h: + +/usr/include/cairo/cairo.h: + +/usr/include/cairo/cairo-features.h: + +/usr/include/cairo/cairo-deprecated.h: + +/usr/include/gtk-2.0/gdk/gdktypes.h: + +/usr/include/glib-2.0/glib.h: + +/usr/include/glib-2.0/glib/galloca.h: + +/usr/include/glib-2.0/glib/gtypes.h: + +/usr/lib/glib-2.0/include/glibconfig.h: + +/usr/include/glib-2.0/glib/gmacros.h: + +/usr/lib/gcc/i486-linux-gnu/4.1.2/include/limits.h: + +/usr/lib/gcc/i486-linux-gnu/4.1.2/include/syslimits.h: + +/usr/include/limits.h: + +/usr/include/bits/posix1_lim.h: + +/usr/include/bits/local_lim.h: + +/usr/include/linux/limits.h: + +/usr/include/bits/posix2_lim.h: + +/usr/lib/gcc/i486-linux-gnu/4.1.2/include/float.h: + +/usr/include/glib-2.0/glib/garray.h: + +/usr/include/glib-2.0/glib/gasyncqueue.h: + +/usr/include/glib-2.0/glib/gthread.h: + +/usr/include/glib-2.0/glib/gerror.h: + +/usr/include/glib-2.0/glib/gquark.h: + +/usr/include/glib-2.0/glib/gatomic.h: + +/usr/include/glib-2.0/glib/gbacktrace.h: + +/usr/include/glib-2.0/glib/gbase64.h: + +/usr/include/glib-2.0/glib/gbookmarkfile.h: + +/usr/include/glib-2.0/glib/gcache.h: + +/usr/include/glib-2.0/glib/glist.h: + +/usr/include/glib-2.0/glib/gmem.h: + +/usr/include/glib-2.0/glib/gslice.h: + +/usr/include/glib-2.0/glib/gcompletion.h: + +/usr/include/glib-2.0/glib/gconvert.h: + +/usr/include/glib-2.0/glib/gdataset.h: + +/usr/include/glib-2.0/glib/gdate.h: + +/usr/include/glib-2.0/glib/gdir.h: + +/usr/include/glib-2.0/glib/gfileutils.h: + +/usr/include/glib-2.0/glib/ghash.h: + +/usr/include/glib-2.0/glib/ghook.h: + +/usr/include/glib-2.0/glib/giochannel.h: + +/usr/include/glib-2.0/glib/gmain.h: + +/usr/include/glib-2.0/glib/gslist.h: + +/usr/include/glib-2.0/glib/gstring.h: + +/usr/include/glib-2.0/glib/gunicode.h: + +/usr/include/glib-2.0/glib/gutils.h: + +/usr/include/glib-2.0/glib/gkeyfile.h: + +/usr/include/glib-2.0/glib/gmappedfile.h: + +/usr/include/glib-2.0/glib/gmarkup.h: + +/usr/include/glib-2.0/glib/gmessages.h: + +/usr/include/glib-2.0/glib/gnode.h: + +/usr/include/glib-2.0/glib/goption.h: + +/usr/include/glib-2.0/glib/gpattern.h: + +/usr/include/glib-2.0/glib/gprimes.h: + +/usr/include/glib-2.0/glib/gqsort.h: + +/usr/include/glib-2.0/glib/gqueue.h: + +/usr/include/glib-2.0/glib/grand.h: + +/usr/include/glib-2.0/glib/grel.h: + +/usr/include/glib-2.0/glib/gscanner.h: + +/usr/include/glib-2.0/glib/gshell.h: + +/usr/include/glib-2.0/glib/gspawn.h: + +/usr/include/glib-2.0/glib/gstrfuncs.h: + +/usr/include/glib-2.0/glib/gthreadpool.h: + +/usr/include/glib-2.0/glib/gtimer.h: + +/usr/include/glib-2.0/glib/gtree.h: + +/usr/include/pango-1.0/pango/pango.h: + +/usr/include/pango-1.0/pango/pango-attributes.h: + +/usr/include/pango-1.0/pango/pango-font.h: + +/usr/include/pango-1.0/pango/pango-coverage.h: + +/usr/include/pango-1.0/pango/pango-types.h: + +/usr/include/glib-2.0/glib-object.h: + +/usr/include/glib-2.0/gobject/gboxed.h: + +/usr/include/glib-2.0/gobject/gtype.h: + +/usr/include/glib-2.0/gobject/genums.h: + +/usr/include/glib-2.0/gobject/gobject.h: + +/usr/include/glib-2.0/gobject/gvalue.h: + +/usr/include/glib-2.0/gobject/gparam.h: + +/usr/include/glib-2.0/gobject/gclosure.h: + +/usr/include/glib-2.0/gobject/gsignal.h: + +/usr/include/glib-2.0/gobject/gmarshal.h: + +/usr/include/glib-2.0/gobject/gparamspecs.h: + +/usr/include/glib-2.0/gobject/gsourceclosure.h: + +/usr/include/glib-2.0/gobject/gtypemodule.h: + +/usr/include/glib-2.0/gobject/gtypeplugin.h: + +/usr/include/glib-2.0/gobject/gvaluearray.h: + +/usr/include/glib-2.0/gobject/gvaluetypes.h: + +/usr/include/pango-1.0/pango/pango-matrix.h: + +/usr/include/pango-1.0/pango/pango-script.h: + +/usr/include/pango-1.0/pango/pango-gravity.h: + +/usr/include/pango-1.0/pango/pango-break.h: + +/usr/include/pango-1.0/pango/pango-item.h: + +/usr/include/pango-1.0/pango/pango-context.h: + +/usr/include/pango-1.0/pango/pango-fontmap.h: + +/usr/include/pango-1.0/pango/pango-fontset.h: + +/usr/include/pango-1.0/pango/pango-engine.h: + +/usr/include/pango-1.0/pango/pango-glyph.h: + +/usr/include/pango-1.0/pango/pango-enum-types.h: + +/usr/include/pango-1.0/pango/pango-features.h: + +/usr/include/pango-1.0/pango/pango-glyph-item.h: + +/usr/include/pango-1.0/pango/pango-layout.h: + +/usr/include/pango-1.0/pango/pango-tabs.h: + +/usr/include/pango-1.0/pango/pango-renderer.h: + +/usr/include/pango-1.0/pango/pango-utils.h: + +/usr/lib/gtk-2.0/include/gdkconfig.h: + +/usr/include/gtk-2.0/gdk/gdkpixbuf.h: + +/usr/include/gtk-2.0/gdk/gdkrgb.h: + +/usr/include/gtk-2.0/gdk-pixbuf/gdk-pixbuf.h: + +/usr/include/gtk-2.0/gdk-pixbuf/gdk-pixbuf-features.h: + +/usr/include/gtk-2.0/gdk-pixbuf/gdk-pixbuf-core.h: + +/usr/include/gtk-2.0/gdk-pixbuf/gdk-pixbuf-transform.h: + +/usr/include/gtk-2.0/gdk-pixbuf/gdk-pixbuf-animation.h: + +/usr/include/gtk-2.0/gdk-pixbuf/gdk-pixbuf-simple-anim.h: + +/usr/include/gtk-2.0/gdk-pixbuf/gdk-pixbuf-io.h: + +/usr/include/glib-2.0/gmodule.h: + +/usr/include/gtk-2.0/gdk-pixbuf/gdk-pixbuf-loader.h: + +/usr/include/gtk-2.0/gdk-pixbuf/gdk-pixbuf-enum-types.h: + +/usr/include/pango-1.0/pango/pangocairo.h: + +/usr/include/gtk-2.0/gdk/gdkcursor.h: + +/usr/include/gtk-2.0/gdk/gdkdisplay.h: + +/usr/include/gtk-2.0/gdk/gdkevents.h: + +/usr/include/gtk-2.0/gdk/gdkdnd.h: + +/usr/include/gtk-2.0/gdk/gdkinput.h: + +/usr/include/gtk-2.0/gdk/gdkdrawable.h: + +/usr/include/gtk-2.0/gdk/gdkgc.h: + +/usr/include/gtk-2.0/gdk/gdkenumtypes.h: + +/usr/include/gtk-2.0/gdk/gdkfont.h: + +/usr/include/gtk-2.0/gdk/gdkimage.h: + +/usr/include/gtk-2.0/gdk/gdkkeys.h: + +/usr/include/gtk-2.0/gdk/gdkdisplaymanager.h: + +/usr/include/gtk-2.0/gdk/gdkpango.h: + +/usr/include/gtk-2.0/gdk/gdkpixmap.h: + +/usr/include/gtk-2.0/gdk/gdkproperty.h: + +/usr/include/gtk-2.0/gdk/gdkregion.h: + +/usr/include/gtk-2.0/gdk/gdkscreen.h: + +/usr/include/gtk-2.0/gdk/gdkselection.h: + +/usr/include/gtk-2.0/gdk/gdkspawn.h: + +/usr/include/gtk-2.0/gdk/gdkvisual.h: + +/usr/include/gtk-2.0/gdk/gdkwindow.h: + +/usr/include/gtk-2.0/gtk/gtkaboutdialog.h: + +/usr/include/gtk-2.0/gtk/gtkdialog.h: + +/usr/include/gtk-2.0/gtk/gtkwindow.h: + +/usr/include/gtk-2.0/gtk/gtkaccelgroup.h: + +/usr/include/gtk-2.0/gtk/gtkenums.h: + +/usr/include/gtk-2.0/gtk/gtkbin.h: + +/usr/include/gtk-2.0/gtk/gtkcontainer.h: + +/usr/include/gtk-2.0/gtk/gtkwidget.h: + +/usr/include/gtk-2.0/gtk/gtkobject.h: + +/usr/include/gtk-2.0/gtk/gtktypeutils.h: + +/usr/include/gtk-2.0/gtk/gtktypebuiltins.h: + +/usr/include/gtk-2.0/gtk/gtkdebug.h: + +/usr/include/gtk-2.0/gtk/gtkadjustment.h: + +/usr/include/gtk-2.0/gtk/gtkstyle.h: + +/usr/include/gtk-2.0/gtk/gtksettings.h: + +/usr/include/gtk-2.0/gtk/gtkrc.h: + +/usr/include/atk-1.0/atk/atkobject.h: + +/usr/include/atk-1.0/atk/atkstate.h: + +/usr/include/atk-1.0/atk/atkrelationtype.h: + +/usr/include/gtk-2.0/gtk/gtkaccellabel.h: + +/usr/include/gtk-2.0/gtk/gtklabel.h: + +/usr/include/gtk-2.0/gtk/gtkmisc.h: + +/usr/include/gtk-2.0/gtk/gtkmenu.h: + +/usr/include/gtk-2.0/gtk/gtkmenushell.h: + +/usr/include/gtk-2.0/gtk/gtkaccelmap.h: + +/usr/include/gtk-2.0/gtk/gtkaccessible.h: + +/usr/include/atk-1.0/atk/atk.h: + +/usr/include/atk-1.0/atk/atkaction.h: + +/usr/include/atk-1.0/atk/atkcomponent.h: + +/usr/include/atk-1.0/atk/atkutil.h: + +/usr/include/atk-1.0/atk/atkdocument.h: + +/usr/include/atk-1.0/atk/atkeditabletext.h: + +/usr/include/atk-1.0/atk/atktext.h: + +/usr/include/atk-1.0/atk/atkgobjectaccessible.h: + +/usr/include/atk-1.0/atk/atkhyperlink.h: + +/usr/include/atk-1.0/atk/atkhyperlinkimpl.h: + +/usr/include/atk-1.0/atk/atkhypertext.h: + +/usr/include/atk-1.0/atk/atkimage.h: + +/usr/include/atk-1.0/atk/atknoopobject.h: + +/usr/include/atk-1.0/atk/atknoopobjectfactory.h: + +/usr/include/atk-1.0/atk/atkobjectfactory.h: + +/usr/include/atk-1.0/atk/atkregistry.h: + +/usr/include/atk-1.0/atk/atkobjectfactory.h: + +/usr/include/atk-1.0/atk/atkrelation.h: + +/usr/include/atk-1.0/atk/atkrelationset.h: + +/usr/include/atk-1.0/atk/atkselection.h: + +/usr/include/atk-1.0/atk/atkstateset.h: + +/usr/include/atk-1.0/atk/atkstreamablecontent.h: + +/usr/include/atk-1.0/atk/atktable.h: + +/usr/include/atk-1.0/atk/atkmisc.h: + +/usr/include/atk-1.0/atk/atkvalue.h: + +/usr/include/gtk-2.0/gtk/gtkaction.h: + +/usr/include/gtk-2.0/gtk/gtkactiongroup.h: + +/usr/include/gtk-2.0/gtk/gtkitemfactory.h: + +/usr/include/gtk-2.0/gtk/gtkalignment.h: + +/usr/include/gtk-2.0/gtk/gtkarrow.h: + +/usr/include/gtk-2.0/gtk/gtkaspectframe.h: + +/usr/include/gtk-2.0/gtk/gtkframe.h: + +/usr/include/gtk-2.0/gtk/gtkassistant.h: + +/usr/include/gtk-2.0/gtk/gtkbbox.h: + +/usr/include/gtk-2.0/gtk/gtkbox.h: + +/usr/include/gtk-2.0/gtk/gtkbindings.h: + +/usr/include/gtk-2.0/gtk/gtkbutton.h: + +/usr/include/gtk-2.0/gtk/gtkimage.h: + +/usr/include/gtk-2.0/gtk/gtkcalendar.h: + +/usr/include/gtk-2.0/gtk/gtksignal.h: + +/usr/include/gtk-2.0/gtk/gtkmarshal.h: + +/usr/include/gtk-2.0/gtk/gtkcelllayout.h: + +/usr/include/gtk-2.0/gtk/gtkcellrenderer.h: + +/usr/include/gtk-2.0/gtk/gtkcelleditable.h: + +/usr/include/gtk-2.0/gtk/gtktreeviewcolumn.h: + +/usr/include/gtk-2.0/gtk/gtktreemodel.h: + +/usr/include/gtk-2.0/gtk/gtktreesortable.h: + +/usr/include/gtk-2.0/gtk/gtkcellrendereraccel.h: + +/usr/include/gtk-2.0/gtk/gtkcellrenderertext.h: + +/usr/include/gtk-2.0/gtk/gtkcellrenderercombo.h: + +/usr/include/gtk-2.0/gtk/gtkcellrenderertext.h: + +/usr/include/gtk-2.0/gtk/gtkcellrendererpixbuf.h: + +/usr/include/gtk-2.0/gtk/gtkcellrendererprogress.h: + +/usr/include/gtk-2.0/gtk/gtkcellrendererspin.h: + +/usr/include/gtk-2.0/gtk/gtkcellrenderertoggle.h: + +/usr/include/gtk-2.0/gtk/gtkcellview.h: + +/usr/include/gtk-2.0/gtk/gtkcheckbutton.h: + +/usr/include/gtk-2.0/gtk/gtktogglebutton.h: + +/usr/include/gtk-2.0/gtk/gtkcheckmenuitem.h: + +/usr/include/gtk-2.0/gtk/gtkmenuitem.h: + +/usr/include/gtk-2.0/gtk/gtkitem.h: + +/usr/include/gtk-2.0/gtk/gtkclipboard.h: + +/usr/include/gtk-2.0/gtk/gtkselection.h: + +/usr/include/gtk-2.0/gtk/gtktextiter.h: + +/usr/include/gtk-2.0/gtk/gtktexttag.h: + +/usr/include/gtk-2.0/gtk/gtktextchild.h: + +/usr/include/gtk-2.0/gtk/gtkclist.h: + +/usr/include/gtk-2.0/gtk/gtkhscrollbar.h: + +/usr/include/gtk-2.0/gtk/gtkscrollbar.h: + +/usr/include/gtk-2.0/gtk/gtkrange.h: + +/usr/include/gtk-2.0/gtk/gtkvscrollbar.h: + +/usr/include/gtk-2.0/gtk/gtkcolorbutton.h: + +/usr/include/gtk-2.0/gtk/gtkcolorsel.h: + +/usr/include/gtk-2.0/gtk/gtkvbox.h: + +/usr/include/gtk-2.0/gtk/gtkcolorseldialog.h: + +/usr/include/gtk-2.0/gtk/gtkcombo.h: + +/usr/include/gtk-2.0/gtk/gtkhbox.h: + +/usr/include/gtk-2.0/gtk/gtkcombobox.h: + +/usr/include/gtk-2.0/gtk/gtktreeview.h: + +/usr/include/gtk-2.0/gtk/gtkdnd.h: + +/usr/include/gtk-2.0/gtk/gtkentry.h: + +/usr/include/gtk-2.0/gtk/gtkeditable.h: + +/usr/include/gtk-2.0/gtk/gtkimcontext.h: + +/usr/include/gtk-2.0/gtk/gtkentrycompletion.h: + +/usr/include/gtk-2.0/gtk/gtkliststore.h: + +/usr/include/gtk-2.0/gtk/gtktreemodelfilter.h: + +/usr/include/gtk-2.0/gtk/gtkcomboboxentry.h: + +/usr/include/gtk-2.0/gtk/gtkctree.h: + +/usr/include/gtk-2.0/gtk/gtkcurve.h: + +/usr/include/gtk-2.0/gtk/gtkdrawingarea.h: + +/usr/include/gtk-2.0/gtk/gtkeventbox.h: + +/usr/include/gtk-2.0/gtk/gtkexpander.h: + +/usr/include/gtk-2.0/gtk/gtkfilesel.h: + +/usr/include/gtk-2.0/gtk/gtkfixed.h: + +/usr/include/gtk-2.0/gtk/gtkfilechooserbutton.h: + +/usr/include/gtk-2.0/gtk/gtkfilechooser.h: + +/usr/include/gtk-2.0/gtk/gtkfilefilter.h: + +/usr/include/gtk-2.0/gtk/gtkfilechooserdialog.h: + +/usr/include/gtk-2.0/gtk/gtkfilechooser.h: + +/usr/include/gtk-2.0/gtk/gtkfilechooserwidget.h: + +/usr/include/gtk-2.0/gtk/gtkfontbutton.h: + +/usr/include/gtk-2.0/gtk/gtkfontsel.h: + +/usr/include/gtk-2.0/gtk/gtkgamma.h: + +/usr/include/gtk-2.0/gtk/gtkgc.h: + +/usr/include/gtk-2.0/gtk/gtkhandlebox.h: + +/usr/include/gtk-2.0/gtk/gtkhbbox.h: + +/usr/include/gtk-2.0/gtk/gtkhpaned.h: + +/usr/include/gtk-2.0/gtk/gtkpaned.h: + +/usr/include/gtk-2.0/gtk/gtkhruler.h: + +/usr/include/gtk-2.0/gtk/gtkruler.h: + +/usr/include/gtk-2.0/gtk/gtkhscale.h: + +/usr/include/gtk-2.0/gtk/gtkscale.h: + +/usr/include/gtk-2.0/gtk/gtkhseparator.h: + +/usr/include/gtk-2.0/gtk/gtkseparator.h: + +/usr/include/gtk-2.0/gtk/gtkiconfactory.h: + +/usr/include/gtk-2.0/gtk/gtkicontheme.h: + +/usr/include/gtk-2.0/gtk/gtkiconview.h: + +/usr/include/gtk-2.0/gtk/gtkimagemenuitem.h: + +/usr/include/gtk-2.0/gtk/gtkimcontextsimple.h: + +/usr/include/gtk-2.0/gtk/gtkimmulticontext.h: + +/usr/include/gtk-2.0/gtk/gtkinputdialog.h: + +/usr/include/gtk-2.0/gtk/gtkinvisible.h: + +/usr/include/gtk-2.0/gtk/gtklayout.h: + +/usr/include/gtk-2.0/gtk/gtklinkbutton.h: + +/usr/include/gtk-2.0/gtk/gtklist.h: + +/usr/include/gtk-2.0/gtk/gtklistitem.h: + +/usr/include/gtk-2.0/gtk/gtkmain.h: + +/usr/include/gtk-2.0/gtk/gtkmenubar.h: + +/usr/include/gtk-2.0/gtk/gtkmenutoolbutton.h: + +/usr/include/gtk-2.0/gtk/gtkmenu.h: + +/usr/include/gtk-2.0/gtk/gtktoolbutton.h: + +/usr/include/gtk-2.0/gtk/gtktoolitem.h: + +/usr/include/gtk-2.0/gtk/gtktooltips.h: + +/usr/include/gtk-2.0/gtk/gtkmessagedialog.h: + +/usr/include/gtk-2.0/gtk/gtkmodules.h: + +/usr/include/gtk-2.0/gtk/gtknotebook.h: + +/usr/include/gtk-2.0/gtk/gtkoldeditable.h: + +/usr/include/gtk-2.0/gtk/gtkoptionmenu.h: + +/usr/include/gtk-2.0/gtk/gtkpixmap.h: + +/usr/include/gtk-2.0/gtk/gtkplug.h: + +/usr/include/gtk-2.0/gtk/gtksocket.h: + +/usr/include/gtk-2.0/gtk/gtkpreview.h: + +/usr/include/gtk-2.0/gtk/gtkprintoperation.h: + +/usr/include/gtk-2.0/gtk/gtkmain.h: + +/usr/include/gtk-2.0/gtk/gtkenums.h: + +/usr/include/gtk-2.0/gtk/gtkwindow.h: + +/usr/include/gtk-2.0/gtk/gtkpagesetup.h: + +/usr/include/gtk-2.0/gtk/gtkpapersize.h: + +/usr/include/gtk-2.0/gtk/gtkprintsettings.h: + +/usr/include/gtk-2.0/gtk/gtkprintcontext.h: + +/usr/include/gtk-2.0/gtk/gtkprintoperationpreview.h: + +/usr/include/gtk-2.0/gtk/gtkprogress.h: + +/usr/include/gtk-2.0/gtk/gtkprogressbar.h: + +/usr/include/gtk-2.0/gtk/gtkradioaction.h: + +/usr/include/gtk-2.0/gtk/gtktoggleaction.h: + +/usr/include/gtk-2.0/gtk/gtkradiobutton.h: + +/usr/include/gtk-2.0/gtk/gtkradiomenuitem.h: + +/usr/include/gtk-2.0/gtk/gtkradiotoolbutton.h: + +/usr/include/gtk-2.0/gtk/gtktoggletoolbutton.h: + +/usr/include/gtk-2.0/gtk/gtktoolbutton.h: + +/usr/include/gtk-2.0/gtk/gtkrecentchooser.h: + +/usr/include/gtk-2.0/gtk/gtkrecentmanager.h: + +/usr/include/gtk-2.0/gtk/gtkrecentfilter.h: + +/usr/include/gtk-2.0/gtk/gtkrecentchooserdialog.h: + +/usr/include/gtk-2.0/gtk/gtkrecentchooser.h: + +/usr/include/gtk-2.0/gtk/gtkrecentchoosermenu.h: + +/usr/include/gtk-2.0/gtk/gtkrecentchooserwidget.h: + +/usr/include/gtk-2.0/gtk/gtkrecentfilter.h: + +/usr/include/gtk-2.0/gtk/gtkrecentmanager.h: + +/usr/include/gtk-2.0/gtk/gtkscrolledwindow.h: + +/usr/include/gtk-2.0/gtk/gtkviewport.h: + +/usr/include/gtk-2.0/gtk/gtkseparatormenuitem.h: + +/usr/include/gtk-2.0/gtk/gtkseparatortoolitem.h: + +/usr/include/gtk-2.0/gtk/gtksizegroup.h: + +/usr/include/gtk-2.0/gtk/gtkspinbutton.h: + +/usr/include/gtk-2.0/gtk/gtkstatusbar.h: + +/usr/include/gtk-2.0/gtk/gtkstatusicon.h: + +/usr/include/gtk-2.0/gtk/gtkstock.h: + +/usr/include/gtk-2.0/gtk/gtktable.h: + +/usr/include/gtk-2.0/gtk/gtktearoffmenuitem.h: + +/usr/include/gtk-2.0/gtk/gtktext.h: + +/usr/include/gtk-2.0/gtk/gtktextbuffer.h: + +/usr/include/gtk-2.0/gtk/gtktexttagtable.h: + +/usr/include/gtk-2.0/gtk/gtktextmark.h: + +/usr/include/gtk-2.0/gtk/gtktextbufferrichtext.h: + +/usr/include/gtk-2.0/gtk/gtktextview.h: + +/usr/include/gtk-2.0/gtk/gtktipsquery.h: + +/usr/include/gtk-2.0/gtk/gtktoggletoolbutton.h: + +/usr/include/gtk-2.0/gtk/gtktoolbar.h: + +/usr/include/gtk-2.0/gtk/gtktoolbutton.h: + +/usr/include/gtk-2.0/gtk/gtktoolitem.h: + +/usr/include/gtk-2.0/gtk/gtktree.h: + +/usr/include/gtk-2.0/gtk/gtktreednd.h: + +/usr/include/gtk-2.0/gtk/gtktreeitem.h: + +/usr/include/gtk-2.0/gtk/gtktreemodelsort.h: + +/usr/include/gtk-2.0/gtk/gtktreeselection.h: + +/usr/include/gtk-2.0/gtk/gtktreestore.h: + +/usr/include/gtk-2.0/gtk/gtkuimanager.h: + +/usr/include/gtk-2.0/gtk/gtkvbbox.h: + +/usr/include/gtk-2.0/gtk/gtkversion.h: + +/usr/include/gtk-2.0/gtk/gtkvpaned.h: + +/usr/include/gtk-2.0/gtk/gtkvruler.h: + +/usr/include/gtk-2.0/gtk/gtkvscale.h: + +/usr/include/gtk-2.0/gtk/gtkvseparator.h: + +../../../src/rvtk/v4l2.h: + +../../../src/rvtk/drv-v4l2.h: + +/usr/include/linux/ioctl.h: + +/usr/include/asm/ioctl.h: + +/usr/include/asm-i386/ioctl.h: + +/usr/include/asm-generic/ioctl.h: + +/usr/include/linux/videodev.h: + +/usr/include/linux/videodev2.h: + +/usr/include/linux/types.h: + +/usr/include/linux/posix_types.h: + +/usr/include/linux/stddef.h: + +/usr/lib/gcc/i486-linux-gnu/4.1.2/include/asm/posix_types.h: + +/usr/include/asm/posix_types.h: + +/usr/include/asm-i386/posix_types.h: + +/usr/include/asm/types.h: + +/usr/include/asm-i386/types.h: + +../../../src/rvtk/fourcc.h: + +../../../src/rvtk/frame.h: + +/usr/include/sys/time.h: + +camera-photo.h: diff --git a/HenocCameraDrv/HenocMouseDriver/opencv/Makefile b/HenocCameraDrv/HenocMouseDriver/opencv/Makefile new file mode 100755 index 0000000..8b59697 --- /dev/null +++ b/HenocCameraDrv/HenocMouseDriver/opencv/Makefile @@ -0,0 +1,468 @@ +# Makefile.in generated by automake 1.10 from Makefile.am. +# src/rvtk/opencv/Makefile. Generated from Makefile.in by configure. + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + + + + +pkgdatadir = $(datadir)/rvtk +pkglibdir = $(libdir)/rvtk +pkgincludedir = $(includedir)/rvtk +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = src/rvtk/opencv +DIST_COMMON = $(librvtk_opencvinclude_HEADERS) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; +am__installdirs = "$(DESTDIR)$(libdir)" \ + "$(DESTDIR)$(librvtk_opencvincludedir)" +libLIBRARIES_INSTALL = $(INSTALL_DATA) +LIBRARIES = $(lib_LIBRARIES) +AR = ar +ARFLAGS = cru +librvtk_opencv_a_AR = $(AR) $(ARFLAGS) +librvtk_opencv_a_LIBADD = +am_librvtk_opencv_a_OBJECTS = ui.$(OBJEXT) cvutils.$(OBJEXT) +librvtk_opencv_a_OBJECTS = $(am_librvtk_opencv_a_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(librvtk_opencv_a_SOURCES) +DIST_SOURCES = $(librvtk_opencv_a_SOURCES) +librvtk_opencvincludeHEADERS_INSTALL = $(INSTALL_HEADER) +HEADERS = $(librvtk_opencvinclude_HEADERS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = ${SHELL} /home/lalo/PruebaC/Rvtk/libv4l2/missing --run aclocal-1.10 +AMTAR = ${SHELL} /home/lalo/PruebaC/Rvtk/libv4l2/missing --run tar +AUTOCONF = ${SHELL} /home/lalo/PruebaC/Rvtk/libv4l2/missing --run autoconf +AUTOHEADER = ${SHELL} /home/lalo/PruebaC/Rvtk/libv4l2/missing --run autoheader +AUTOMAKE = ${SHELL} /home/lalo/PruebaC/Rvtk/libv4l2/missing --run automake-1.10 +AWK = gawk +CC = gcc +CCDEPMODE = depmode=gcc3 +CFLAGS = -g -O2 +CPP = gcc -E +CPPFLAGS = +CXX = g++ +CXXDEPMODE = depmode=gcc3 +CXXFLAGS = -g -O2 +CYGPATH_W = echo +DEFS = -DHAVE_CONFIG_H +DEPDIR = .deps +ECHO_C = +ECHO_N = -n +ECHO_T = +EGREP = /bin/grep -E +EXEEXT = +GREP = /bin/grep +INSTALL = /usr/bin/install -c +INSTALL_DATA = ${INSTALL} -m 644 +INSTALL_PROGRAM = ${INSTALL} +INSTALL_SCRIPT = ${INSTALL} +INSTALL_STRIP_PROGRAM = $(install_sh) -c -s +LDFLAGS = +LIBOBJS = +LIBS = +LTLIBOBJS = +MAINT = # +MAKEINFO = ${SHELL} /home/lalo/PruebaC/Rvtk/libv4l2/missing --run makeinfo +MKDIR_P = /bin/mkdir -p +OBJEXT = o +PACKAGE = rvtk +PACKAGE_BUGREPORT = +PACKAGE_CFLAGS = -I/usr/include/gtk-2.0 -I/usr/lib/gtk-2.0/include -I/usr/include/atk-1.0 -I/usr/include/cairo -I/usr/include/pango-1.0 -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/freetype2 -I/usr/include/libpng12 -I/usr/include/libglade-2.0 -I/usr/include/libxml2 -I/usr/include/opencv +PACKAGE_LIBS = -lglade-2.0 -lgtk-x11-2.0 -lxml2 -lgdk-x11-2.0 -latk-1.0 -lgdk_pixbuf-2.0 -lm -lpangocairo-1.0 -lfontconfig -lXext -lXrender -lXinerama -lXi -lXrandr -lXcursor -lXfixes -lpango-1.0 -lcairo -lX11 -lgobject-2.0 -lgmodule-2.0 -ldl -lglib-2.0 -lcxcore -lcv -lhighgui -lcvaux -lml +PACKAGE_NAME = +PACKAGE_STRING = +PACKAGE_TARNAME = +PACKAGE_VERSION = +PATH_SEPARATOR = : +PKG_CONFIG = /usr/bin/pkg-config +RANLIB = ranlib +SET_MAKE = +SHELL = /bin/sh +STRIP = +VERSION = 0.1 +abs_builddir = /home/lalo/PruebaC/Rvtk/libv4l2/src/rvtk/opencv +abs_srcdir = /home/lalo/PruebaC/Rvtk/libv4l2/src/rvtk/opencv +abs_top_builddir = /home/lalo/PruebaC/Rvtk/libv4l2 +abs_top_srcdir = /home/lalo/PruebaC/Rvtk/libv4l2 +ac_ct_CC = gcc +ac_ct_CXX = g++ +am__include = include +am__leading_dot = . +am__quote = +am__tar = ${AMTAR} chof - "$$tardir" +am__untar = ${AMTAR} xf - +bindir = ${exec_prefix}/bin +build_alias = +builddir = . +datadir = ${datarootdir} +datarootdir = ${prefix}/share +docdir = ${datarootdir}/doc/${PACKAGE} +dvidir = ${docdir} +exec_prefix = ${prefix} +host_alias = +htmldir = ${docdir} +includedir = ${prefix}/include +infodir = ${datarootdir}/info +install_sh = $(SHELL) /home/lalo/PruebaC/Rvtk/libv4l2/install-sh +libdir = ${exec_prefix}/lib +libexecdir = ${exec_prefix}/libexec +localedir = ${datarootdir}/locale +localstatedir = ${prefix}/var +mandir = ${datarootdir}/man +mkdir_p = /bin/mkdir -p +oldincludedir = /usr/include +pdfdir = ${docdir} +prefix = /usr/local +program_transform_name = s,x,x, +psdir = ${docdir} +sbindir = ${exec_prefix}/sbin +sharedstatedir = ${prefix}/com +srcdir = . +sysconfdir = ${prefix}/etc +target_alias = +top_builddir = ../../.. +top_srcdir = ../../.. +INCLUDES = -DPACKAGE_DATA_DIR=\""$(pkgdatadir)"\" \ + -I/usr/include/gtk-2.0 -I/usr/lib/gtk-2.0/include -I/usr/include/atk-1.0 -I/usr/include/cairo -I/usr/include/pango-1.0 -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/freetype2 -I/usr/include/libpng12 -I/usr/include/libglade-2.0 -I/usr/include/libxml2 -I/usr/include/opencv \ + -I${top_srcdir}/src + +lib_LIBRARIES = librvtk-opencv.a +librvtk_opencv_a_DEPENDENCIES = ${top_srcdir}/src/rvtk/librvtk-vc.a +librvtk_opencv_a_SOURCES = ui.c cvutils.c +librvtk_opencvincludedir = ${includedir}/rvtk/opencv +librvtk_opencvinclude_HEADERS = cv-v4l2.h \ + ui.h \ + cvutils.h + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .o .obj +$(srcdir)/Makefile.in: # $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/rvtk/opencv/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/rvtk/opencv/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: # $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): # $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-libLIBRARIES: $(lib_LIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)" + @list='$(lib_LIBRARIES)'; for p in $$list; do \ + if test -f $$p; then \ + f=$(am__strip_dir) \ + echo " $(libLIBRARIES_INSTALL) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \ + $(libLIBRARIES_INSTALL) "$$p" "$(DESTDIR)$(libdir)/$$f"; \ + else :; fi; \ + done + @$(POST_INSTALL) + @list='$(lib_LIBRARIES)'; for p in $$list; do \ + if test -f $$p; then \ + p=$(am__strip_dir) \ + echo " $(RANLIB) '$(DESTDIR)$(libdir)/$$p'"; \ + $(RANLIB) "$(DESTDIR)$(libdir)/$$p"; \ + else :; fi; \ + done + +uninstall-libLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LIBRARIES)'; for p in $$list; do \ + p=$(am__strip_dir) \ + echo " rm -f '$(DESTDIR)$(libdir)/$$p'"; \ + rm -f "$(DESTDIR)$(libdir)/$$p"; \ + done + +clean-libLIBRARIES: + -test -z "$(lib_LIBRARIES)" || rm -f $(lib_LIBRARIES) +librvtk-opencv.a: $(librvtk_opencv_a_OBJECTS) $(librvtk_opencv_a_DEPENDENCIES) + -rm -f librvtk-opencv.a + $(librvtk_opencv_a_AR) librvtk-opencv.a $(librvtk_opencv_a_OBJECTS) $(librvtk_opencv_a_LIBADD) + $(RANLIB) librvtk-opencv.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +include ./$(DEPDIR)/cvutils.Po +include ./$(DEPDIR)/ui.Po + +.c.o: + $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< + mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +# source='$<' object='$@' libtool=no \ +# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ +# $(COMPILE) -c $< + +.c.obj: + $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` + mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +# source='$<' object='$@' libtool=no \ +# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ +# $(COMPILE) -c `$(CYGPATH_W) '$<'` +install-librvtk_opencvincludeHEADERS: $(librvtk_opencvinclude_HEADERS) + @$(NORMAL_INSTALL) + test -z "$(librvtk_opencvincludedir)" || $(MKDIR_P) "$(DESTDIR)$(librvtk_opencvincludedir)" + @list='$(librvtk_opencvinclude_HEADERS)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f=$(am__strip_dir) \ + echo " $(librvtk_opencvincludeHEADERS_INSTALL) '$$d$$p' '$(DESTDIR)$(librvtk_opencvincludedir)/$$f'"; \ + $(librvtk_opencvincludeHEADERS_INSTALL) "$$d$$p" "$(DESTDIR)$(librvtk_opencvincludedir)/$$f"; \ + done + +uninstall-librvtk_opencvincludeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(librvtk_opencvinclude_HEADERS)'; for p in $$list; do \ + f=$(am__strip_dir) \ + echo " rm -f '$(DESTDIR)$(librvtk_opencvincludedir)/$$f'"; \ + rm -f "$(DESTDIR)$(librvtk_opencvincludedir)/$$f"; \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(librvtk_opencvincludedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: install-librvtk_opencvincludeHEADERS + +install-dvi: install-dvi-am + +install-exec-am: install-libLIBRARIES + +install-html: install-html-am + +install-info: install-info-am + +install-man: + +install-pdf: install-pdf-am + +install-ps: install-ps-am + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-libLIBRARIES \ + uninstall-librvtk_opencvincludeHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libLIBRARIES ctags distclean distclean-compile \ + distclean-generic distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-libLIBRARIES \ + install-librvtk_opencvincludeHEADERS install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \ + uninstall-am uninstall-libLIBRARIES \ + uninstall-librvtk_opencvincludeHEADERS + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/HenocCameraDrv/HenocMouseDriver/opencv/Makefile.am b/HenocCameraDrv/HenocMouseDriver/opencv/Makefile.am new file mode 100755 index 0000000..33c54c8 --- /dev/null +++ b/HenocCameraDrv/HenocMouseDriver/opencv/Makefile.am @@ -0,0 +1,15 @@ +## Process this file with automake to produce Makefile.in + +INCLUDES = -DPACKAGE_DATA_DIR=\""$(pkgdatadir)"\" \ + @PACKAGE_CFLAGS@ \ + -I${top_srcdir}/src + +lib_LIBRARIES = librvtk-opencv.a + +librvtk_opencv_a_DEPENDENCIES = ${top_srcdir}/src/rvtk/librvtk-vc.a +librvtk_opencv_a_SOURCES = ui.c cvutils.c + +librvtk_opencvincludedir = ${includedir}/rvtk/opencv +librvtk_opencvinclude_HEADERS=cv-v4l2.h \ + ui.h \ + cvutils.h diff --git a/HenocCameraDrv/HenocMouseDriver/opencv/Makefile.in b/HenocCameraDrv/HenocMouseDriver/opencv/Makefile.in new file mode 100755 index 0000000..9f54a19 --- /dev/null +++ b/HenocCameraDrv/HenocMouseDriver/opencv/Makefile.in @@ -0,0 +1,468 @@ +# Makefile.in generated by automake 1.10 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = src/rvtk/opencv +DIST_COMMON = $(librvtk_opencvinclude_HEADERS) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; +am__installdirs = "$(DESTDIR)$(libdir)" \ + "$(DESTDIR)$(librvtk_opencvincludedir)" +libLIBRARIES_INSTALL = $(INSTALL_DATA) +LIBRARIES = $(lib_LIBRARIES) +AR = ar +ARFLAGS = cru +librvtk_opencv_a_AR = $(AR) $(ARFLAGS) +librvtk_opencv_a_LIBADD = +am_librvtk_opencv_a_OBJECTS = ui.$(OBJEXT) cvutils.$(OBJEXT) +librvtk_opencv_a_OBJECTS = $(am_librvtk_opencv_a_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(top_builddir)@am__isrc@ +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(librvtk_opencv_a_SOURCES) +DIST_SOURCES = $(librvtk_opencv_a_SOURCES) +librvtk_opencvincludeHEADERS_INSTALL = $(INSTALL_HEADER) +HEADERS = $(librvtk_opencvinclude_HEADERS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_CFLAGS = @PACKAGE_CFLAGS@ +PACKAGE_LIBS = @PACKAGE_LIBS@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +INCLUDES = -DPACKAGE_DATA_DIR=\""$(pkgdatadir)"\" \ + @PACKAGE_CFLAGS@ \ + -I${top_srcdir}/src + +lib_LIBRARIES = librvtk-opencv.a +librvtk_opencv_a_DEPENDENCIES = ${top_srcdir}/src/rvtk/librvtk-vc.a +librvtk_opencv_a_SOURCES = ui.c cvutils.c +librvtk_opencvincludedir = ${includedir}/rvtk/opencv +librvtk_opencvinclude_HEADERS = cv-v4l2.h \ + ui.h \ + cvutils.h + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/rvtk/opencv/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/rvtk/opencv/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-libLIBRARIES: $(lib_LIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)" + @list='$(lib_LIBRARIES)'; for p in $$list; do \ + if test -f $$p; then \ + f=$(am__strip_dir) \ + echo " $(libLIBRARIES_INSTALL) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \ + $(libLIBRARIES_INSTALL) "$$p" "$(DESTDIR)$(libdir)/$$f"; \ + else :; fi; \ + done + @$(POST_INSTALL) + @list='$(lib_LIBRARIES)'; for p in $$list; do \ + if test -f $$p; then \ + p=$(am__strip_dir) \ + echo " $(RANLIB) '$(DESTDIR)$(libdir)/$$p'"; \ + $(RANLIB) "$(DESTDIR)$(libdir)/$$p"; \ + else :; fi; \ + done + +uninstall-libLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LIBRARIES)'; for p in $$list; do \ + p=$(am__strip_dir) \ + echo " rm -f '$(DESTDIR)$(libdir)/$$p'"; \ + rm -f "$(DESTDIR)$(libdir)/$$p"; \ + done + +clean-libLIBRARIES: + -test -z "$(lib_LIBRARIES)" || rm -f $(lib_LIBRARIES) +librvtk-opencv.a: $(librvtk_opencv_a_OBJECTS) $(librvtk_opencv_a_DEPENDENCIES) + -rm -f librvtk-opencv.a + $(librvtk_opencv_a_AR) librvtk-opencv.a $(librvtk_opencv_a_OBJECTS) $(librvtk_opencv_a_LIBADD) + $(RANLIB) librvtk-opencv.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cvutils.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` +install-librvtk_opencvincludeHEADERS: $(librvtk_opencvinclude_HEADERS) + @$(NORMAL_INSTALL) + test -z "$(librvtk_opencvincludedir)" || $(MKDIR_P) "$(DESTDIR)$(librvtk_opencvincludedir)" + @list='$(librvtk_opencvinclude_HEADERS)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f=$(am__strip_dir) \ + echo " $(librvtk_opencvincludeHEADERS_INSTALL) '$$d$$p' '$(DESTDIR)$(librvtk_opencvincludedir)/$$f'"; \ + $(librvtk_opencvincludeHEADERS_INSTALL) "$$d$$p" "$(DESTDIR)$(librvtk_opencvincludedir)/$$f"; \ + done + +uninstall-librvtk_opencvincludeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(librvtk_opencvinclude_HEADERS)'; for p in $$list; do \ + f=$(am__strip_dir) \ + echo " rm -f '$(DESTDIR)$(librvtk_opencvincludedir)/$$f'"; \ + rm -f "$(DESTDIR)$(librvtk_opencvincludedir)/$$f"; \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(librvtk_opencvincludedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: install-librvtk_opencvincludeHEADERS + +install-dvi: install-dvi-am + +install-exec-am: install-libLIBRARIES + +install-html: install-html-am + +install-info: install-info-am + +install-man: + +install-pdf: install-pdf-am + +install-ps: install-ps-am + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-libLIBRARIES \ + uninstall-librvtk_opencvincludeHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libLIBRARIES ctags distclean distclean-compile \ + distclean-generic distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-libLIBRARIES \ + install-librvtk_opencvincludeHEADERS install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \ + uninstall-am uninstall-libLIBRARIES \ + uninstall-librvtk_opencvincludeHEADERS + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/HenocCameraDrv/HenocMouseDriver/opencv/camera-photo.h b/HenocCameraDrv/HenocMouseDriver/opencv/camera-photo.h new file mode 100755 index 0000000..45b3745 --- /dev/null +++ b/HenocCameraDrv/HenocMouseDriver/opencv/camera-photo.h @@ -0,0 +1,114 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (my_pixbuf) +#endif +#ifdef __GNUC__ +static const guint8 camera_photo[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 camera_photo[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (2643) */ + "\0\0\12k" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (128) */ + "\0\0\0\200" + /* width (32) */ + "\0\0\0\40" + /* height (32) */ + "\0\0\0\40" + /* pixel_data: */ + "\320\0\0\0\0\2\241\241\2419\244\244\244\362\204\241\241\241\377\2\245" + "\245\245\352\240\240\240#\230\0\0\0\0\2\236\236\236\330\335\335\335\377" + "\204\377\377\377\377\2\322\322\322\377\231\233\231\250\230\0\0\0\0\2" + "\253\253\252\347\341\341\341\377\204\366\366\366\377\2\333\333\333\377" + "\242\243\242\303\230\0\0\0\0\2{}z\344\324\325\324\377\204\325\325\325" + "\377\2\322\322\322\377pqo\277\227\0\0\0\0\3F]F\13PSO\375SVR\377\204[" + "]Z\377\2RUQ\377PSN\352\227\0\0\0\0\3""4946685\377685\263\204<<<\21\3" + "675\270685\377666!\226\0\0\0\0\3!!!\225\34\35\34\377$$$\377\204(((\377" + "\3$%$\377\34\35\34\377!!!\212\225\0\0\0\0\14)))j\"#\"\372Z[X\377\220" + "\220\216\377\222\222\220\377\225\226\222\377\230\230\225\377\232\232" + "\227\377\233\234\230\377564\377\36\36\36\372(((\77\213\0\0\0\0\31""3" + "33\5'''.(((S(((x)))\234)))\273)))\323)))\340(((\361NNN\373\232\233\230" + "\377\202\203\200\377\\]Y\377`a]\377efa\377jkf\377opk\377sto\377\207\210" + "\205\377NOL\377(((\377+,+\353,,+\334))){,,,\35\204\0\0\0\0\23@@@\4))" + ")c(((\320)))\371443\366FFF\366__^\374tus\377\205\206\204\377\222\224" + "\221\377\237\240\235\377\250\251\246\377\247\250\246\377\206\211\205" + "\377WXT\377RSO\377WXT\377]^Y\377bc^\377\202ghc\377\10fhc\377\177\200" + "}\377\217\220\215\377\207\210\205\377{{x\377UUS\371+++\366+++r\202\0" + "\0\0\0\13,,,#)))\357JJJ\376\207\210\205\377\247\250\245\377\260\261\256" + "\377\254\256\252\377\246\250\244\377\240\241\235\377\231\233\227\377" + "``_\377\202FFF\377\2XXW\377tvr\377\207\247\253\247\377\10\245\251\245" + "\377sto\377tup\377rso\377z{w\377\215\215\212\377\177\177|\377(((\377" + "\202\0\0\0\0\13)))\342}~|\377\260\261\256\377\234\235\231\377\216\220" + "\213\377prm\377TVT\377VXT\377\210\212\205\377\222\224\220\377FFF\377" + "\202\377\377\377\377\7FFF\377\204\210\204\377[[X\377XXU\377\\]Y\377b" + "c^\377fgc\377\202klg\377\10nok\377\212\214\210\377ghc\377lmh\377ssr\377" + "tur\377\223\223\220\377(((\377\202\0\0\0\0\3(((\377\255\256\253\377\212" + "\214\207\377\202\210\212\205\377\6]^[\377PQP\3779:8\377\221\222\215\377" + "jjg\377FFF\377\202\347\347\347\377\21FFF\377uxt\377NNK\377_ed\377enq" + "\377^in\377U_d\377MUY\377KQS\377TXV\377wxt\377sup\377ijf\377]]]\377b" + "ba\377\221\221\216\377(((\377\202\0\0\0\0\13)))\373\233\234\233\377\230" + "\232\227\377\215\216\212\377\210\213\205\377\210\212\205\377\204\207" + "\201\377\221\222\216\377\215\220\213\377674\377HHG\377\202III\377\14" + "YZX\377QSR\377s\177\203\377\202\220\225\377\214\225\227\377\210\215\220" + "\377|\200\201\377psu\377_dg\377AIL\377:@B\377uxt\377\203ghc\377\2\216" + "\217\213\377(((\377\202\0\0\0\0\32)))\373\216\217\216\377\\`[\377\202" + "\205\201\377\232\234\231\377\241\243\240\377\223\226\222\377ilh\377<" + ">:\377563\377553\377775\377BB\77\377bdb\377kw|\377\243\255\257\377\217" + "\224\225\377]bc\377TWX\377QTU\377QUV\377PTU\377bdd\377\\_`\377:\77C\377" + "hkg\377\202ghc\377\2\215\215\212\377(((\377\202\0\0\0\0\3)))\373\213" + "\214\213\377X[W\377\202TWS\377\31RUQ\377KNJ\377CFB\377<>:\377563\377" + "553\377775\377QRN\377]gh\377\214\222\224\377SWY\377klm\377hij\377TWX" + "\377LOP\377UXY\377kll\377hii\377DIJ\377]`c\377>CE\377fgb\377ghc\377\213" + "\214\210\377(((\377\202\0\0\0\0\3)))\373\211\212\211\377X[W\377\202T" + "WS\377\31RUQ\377KNJ\377CFB\377<>:\377563\377553\377775\377GLK\377qwx" + "\377\77DF\377qqr\377@CE\377\21\24\25\377\6\6\6\377\12\12\12\377\23\23" + "\24\377\"%%\377HKM\3774:<\37739;\377RUW\377RTS\377ghc\377\210\211\205" + "\377(((\377\202\0\0\0\0\3)))\372\206\210\206\377WZV\377\202TWS\377\22" + "RUQ\377KNJ\377CFB\377<>:\377563\377553\377775\377PVV\377\77DE\377.46" + "\377+01\377\3\3\3\377\4\4\4\377\3\3\3\377\1\1\1\377\5\5\5\377\30\30\30" + "\377)**\377\202.46\377\5;@A\377JNP\377jkg\377\207\207\203\377(((\377" + "\202\0\0\0\0\3)))\372\202\204\202\377Z]Y\377\202TWS\377\23RUQ\377KNJ" + "\377CFB\377<>:\377563\377553\377775\377QWW\377.46\377-35\377\6\7\7\377" + "\25\25\25\377~~~\377[[[\377000\377\21\21\21\377\22\22\22\377\40\40\40" + "\377789\377\202.46\377\4GLN\377\233\233\232\377\206\206\202\377(((\377" + "\202\0\0\0\0\3)))\373\200\201\177\377[^Z\377\202TWS\377\31RUQ\377KNJ" + "\377CFB\377<>:\377563\377553\377775\377BFH\377.46\377\37#%\377\12\13" + "\13\377CCC\377\274\274\274\377\233\233\233\377\304\304\304\377PPP\377" + "***\377###\377AAA\37749;\377.46\377=BD\377\223\224\223\377\222\222\217" + "\377(((\377\202\0\0\0\0\3(((\377~\177~\377[^Z\377\202TWS\377\15RUQ\377" + "KNJ\377CFB\377<>:\377563\377553\377775\3775;<\377.46\377\31\33\34\377" + "\11\11\11\377@@@\377~~~\377\202\340\340\340\377\12\200\200\200\377YY" + "Y\377888\377AAA\377BFG\377.46\3774:;\377\235\235\235\377\207\210\205" + "\377(((\377\202\0\0\0\0\3)))\377|}|\377Z]Y\377\202TWS\377\31RUQ\377K" + "NJ\377CFB\377<>:\377563\377553\377775\377068\377.46\377\37\40!\377\14" + "\14\14\377\25\25\25\377\202\202\202\377\333\333\333\377\255\255\255\377" + "\213\213\213\377ttt\377KKK\377LLL\377JMN\377.46\377068\377~~{\377\177" + "\200{\377(((\377\202\0\0\0\0\3)))\377xzx\377[^Z\377\202TWS\377HRUQ\377" + "KNJ\377CFB\377=\77;\377]][\377__]\377^^]\3778>\77\377.46\377),-\377\31" + "\31\31\377\13\13\13\377$$$\377lll\377\211\211\211\377\224\224\224\377" + "sss\377XXX\377\\]]\377CGH\377.46\3777>>\377xyu\377uuq\377)))\372\0\0" + "\0\2\0\0\0\1)))\372ghf\377x{w\377bea\377Y\\X\377SVR\377ORN\377RUQ\377" + "ac`\377TUS\377443\371443\3639<>\373.46\377/46\377444\377\17\17\17\377" + "\"\"\"\377;;;\377\\\\\\\377hhh\377jjj\377\\\\\\\377kll\377068\377.46" + "\3778;=\373;;:\362---\366&&&\222\0\0\0\24\0\0\0\7%%%\230//.\376XYW\377" + "qsp\377|\177{\377}\177|\377tvs\377bdb\377EFD\377(((\366\32\32\32\240" + "\24\24\24\215\33\37\37\247\202.46\377\11""6:;\377@@@\377$$$\377111\377" + "DDD\377RRR\377[[[\377sss\377EIK\377\202.46\377\20\32\35\35\262\24\24" + "\24\214\20\20\20`\0\0\0/\0\0\0\33\0\0\0\2\0\0\0\24\27\27\27N###\252&" + "&&\335+++\365...\374)))\357%%%\320\35\35\35\234\11\11\11V\203\0\0\0K" + "\1+/1\302\202.46\377\7>BC\377YYY\377UVV\377YYY\377iii\377stt\377GKL\377" + "\202.46\377\14(+-\310\0\0\0_\0\0\0R\0\0\0""9\0\0\0'\0\0\0\23\0\0\0\0" + "\0\0\0\2\0\0\0\20\0\0\0\36\0\0\0&\0\0\0+\210\0\0\0,\2\5\5\5""3.34\315" + "\202.46\377\5""057\377\77DE\377GKM\377@EF\377/57\377\202.46\377\6,12" + "\322\3\3\3N\0\0\0<\0\0\0,\0\0\0\34\0\0\0\12\205\0\0\0\0\1\0\0\0\2\212" + "\0\0\0\6\3\34\34\34\11+13\203/67\367\205.46\377\6/67\367',.\220\7\7\7" + "$\0\0\0\27\0\0\0\7\0\0\0\2\224\0\0\0\0\11---\21,25\\,35\221-24\243+2" + "4\224).1c\34\34\34\33\0\0\0\7\0\0\0\2\246\0\0\0\0"}; + + diff --git a/HenocCameraDrv/HenocMouseDriver/opencv/cv-v4l2.h b/HenocCameraDrv/HenocMouseDriver/opencv/cv-v4l2.h new file mode 100755 index 0000000..9b0bcbe --- /dev/null +++ b/HenocCameraDrv/HenocMouseDriver/opencv/cv-v4l2.h @@ -0,0 +1,8 @@ +#ifndef CVV4L2_H_ +#define CVV4L2_H_ + +#include +#include +#include + +#endif /*CVV4L2_H_*/ diff --git a/HenocCameraDrv/HenocMouseDriver/opencv/cvutils.c b/HenocCameraDrv/HenocMouseDriver/opencv/cvutils.c new file mode 100755 index 0000000..98a3ace --- /dev/null +++ b/HenocCameraDrv/HenocMouseDriver/opencv/cvutils.c @@ -0,0 +1,83 @@ +#include +#include "cvutils.h" + +void v4l2FrameToIplImage(VidFrame *frame,IplImage *image){ + memset(image,0,sizeof(IplImage)); + + image->nSize = sizeof(IplImage); + + image->ID = 0; + + image->nChannels = 3; //FIXME. Should implement channel() determination func in GVideoPixel + + image->alphaChannel = 0 ; /* ignored by OpenCV */ + + image->depth=IPL_DEPTH_8U; //FIXME + /* pixel depth in bits: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16S, + IPL_DEPTH_32S, IPL_DEPTH_32F and IPL_DEPTH_64F are supported */ + //image->colorModel[4]; /* ignored by OpenCV */ + //image->channelSeq[4]; /* ditto */ + + image->dataOrder=0; /* 0 - interleaved color channels, 1 - separate color channels. + cvCreateImage can only create interleaved images */ + image->origin=0; /* 0 - top-left origin, + 1 - bottom-left origin (Windows bitmaps style) */ + //image->align; /* Alignment of image rows (4 or 8). OpenCV ignores it and uses widthStep instead */ + + + image->roi = 0 ;/* image ROI. if NULL, the whole image is selected */ + + image->maskROI=0; /* must be NULL */ + + image->imageId = 0; /* ditto */ + + image->tileInfo=0; /* ditto */ + + image->width = frame->size.width; /* image width in pixels */ + + image->height = frame->size.height; /* image height in pixels */ + + image->imageSize = frame->imagesize; /* image data size in bytes + (==image->height*image->widthStep + in case of interleaved data)*/ + + image->imageData =(char*) frame->data; /* pointer to a ligned image data */ + + //image->widthStep = frame->bytesperline; /* size of aligned image row in bytes */ + + image->widthStep = frame->bytesperline; /* size of aligned image row in bytes */ + + //image->BorderMode[4]; /* ignored by OpenCV */ + //image->BorderConst[4]; /* ditto */ + + image->imageDataOrigin=0 ; //FIXME +} + + +/** + * @todo Deprecate + **/ + +IplImage * v4l2CaptureQueryIplImage(V4L2Capture *capture,IplImage *data){ + static VidFrame *tmp=0; + VidConv *conv; + VidFrame *input; + IplImage *output=0; + + if (!tmp ) { + tmp = vidFrameCreate(); + } + + conv = vidConvFind(v4l2CaptureGetImageFormat(capture),V4L2_PIX_FMT_BGR24); + + if (conv){ + input = v4l2CaptureQueryFrame(capture); + vidConvProcess(conv,input,tmp); + v4l2FrameToIplImage(tmp,data); + + output = data; + } + + return output; +} + diff --git a/HenocCameraDrv/HenocMouseDriver/opencv/cvutils.h b/HenocCameraDrv/HenocMouseDriver/opencv/cvutils.h new file mode 100755 index 0000000..3ed56f3 --- /dev/null +++ b/HenocCameraDrv/HenocMouseDriver/opencv/cvutils.h @@ -0,0 +1,42 @@ +#ifndef CVUTILS_H_ +#define CVUTILS_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* defined(__cplusplus) */ + +typedef enum { + VID_CAPTURE_TYPE_V4L2, + VID_CAPTURE_TYPE_CV, +} VIDCaptureType; + +/// Prototype of VIDCapture structure for future use + +typedef struct { + /// Type of capture source. + VIDCaptureType type; + + union { + V4L2Capture *v4l2; + CvCapture *cv; + } capture; + + VidConv *converter; + +} VIDCapture; + +/// Convert a V4L2Frame to IplImage +void v4l2FrameToIplImage(VidFrame *frame,IplImage *image); + +/// Grab a frame and convert to IplImage *. +IplImage * v4l2CaptureQueryIplImage(V4L2Capture *capture,IplImage *buffer); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* defined(__cplusplus) */ + +#endif /*CVUTILS_H_*/ diff --git a/HenocCameraDrv/HenocMouseDriver/opencv/ui.c b/HenocCameraDrv/HenocMouseDriver/opencv/ui.c new file mode 100755 index 0000000..6ed92c8 --- /dev/null +++ b/HenocCameraDrv/HenocMouseDriver/opencv/ui.c @@ -0,0 +1,541 @@ +#include +#include +#include + +#include "ui.h" +#include "camera-photo.h" + +static void capture_source_auto_detection(GtkIconView *iconview){ + GtkListStore *store; + GtkTreeIter iter; + store = gtk_list_store_new (3, G_TYPE_STRING, GDK_TYPE_PIXBUF,G_TYPE_STRING); + GdkPixbuf *pixbuf; + char buf[1024]; + + int i; + + /// Camera Photo Image from Tango Project + pixbuf = gdk_pixbuf_new_from_inline(-1,camera_photo,0,0); + + V4L2Capture *capture ; + + for (i=0;i<64;i++){ + snprintf(buf,sizeof(buf),"/dev/video%d",i); + capture = v4l2CaptureOpen(buf); + if (capture){ + + gtk_list_store_append(store , &iter); + snprintf (buf,sizeof(buf), + "%s \n[%s]", + v4l2CaputreGetName(capture), + v4l2CaputreGetLocation(capture)); + + gtk_list_store_set (store, &iter, + 0, buf, + 1, pixbuf, + 2, v4l2CaputreGetLocation(capture), + -1); + + v4l2CaptureRelease(&capture); + } + } + + gtk_icon_view_set_model (GTK_ICON_VIEW (iconview), GTK_TREE_MODEL (store)); + gtk_icon_view_set_text_column (GTK_ICON_VIEW (iconview), 0); + gtk_icon_view_set_pixbuf_column (GTK_ICON_VIEW (iconview), 1); + + g_object_unref (G_OBJECT (store)); + g_object_unref(G_OBJECT(pixbuf)); + +} + +/** + * @param isV4L2Device - Return whether is the input source a V4L2 device + * @Return A newly allocated string contains the location of the capture source(should be a file other a video device). It must be released by user. + */ + +char *capture_source_chooser_run(int *isV4L2Device){ + GtkWidget *CaptureSourceChooserDialog; + GtkWidget *Notebook; + GtkWidget *scrolledwindow3; + GtkWidget *VideoDevicesIconview; + GtkWidget *label; + GtkWidget *FileChooser; + GtkWidget *dialog_action_area1; + GtkWidget *cancelbutton1; + GtkWidget *okbutton1; + + CaptureSourceChooserDialog = gtk_dialog_new (); + gtk_window_set_title (GTK_WINDOW (CaptureSourceChooserDialog), "ConnectTo"); + gtk_window_set_type_hint (GTK_WINDOW (CaptureSourceChooserDialog), GDK_WINDOW_TYPE_HINT_DIALOG); + + GtkWidget *dialog_vbox1; + dialog_vbox1 = GTK_DIALOG (CaptureSourceChooserDialog)->vbox; + gtk_widget_show (dialog_vbox1); + + Notebook = gtk_notebook_new (); + gtk_widget_show (Notebook); + gtk_box_pack_start (GTK_BOX (dialog_vbox1), Notebook, TRUE, TRUE, 0); + gtk_notebook_set_tab_pos (GTK_NOTEBOOK (Notebook), GTK_POS_LEFT); + + scrolledwindow3 = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_show (scrolledwindow3); + gtk_container_add (GTK_CONTAINER (Notebook), scrolledwindow3); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow3), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow3), GTK_SHADOW_IN); + + VideoDevicesIconview = gtk_icon_view_new (); + + gtk_icon_view_set_selection_mode(GTK_ICON_VIEW(VideoDevicesIconview), + GTK_SELECTION_SINGLE); + + gtk_icon_view_set_item_width(GTK_ICON_VIEW(VideoDevicesIconview),-1); + + gtk_widget_show (VideoDevicesIconview); + gtk_container_add (GTK_CONTAINER (scrolledwindow3), VideoDevicesIconview); + + label = gtk_label_new ("Video Devices"); + gtk_widget_show (label); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (Notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (Notebook), 0), label); + + FileChooser = gtk_file_chooser_widget_new (GTK_FILE_CHOOSER_ACTION_OPEN); + gtk_widget_show (FileChooser); + gtk_container_add (GTK_CONTAINER (Notebook), FileChooser); + gtk_widget_set_size_request (FileChooser, 500, 300); + + label = gtk_label_new ("Multimedia Files"); + gtk_widget_show (label); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (Notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (Notebook), 1), label); + + dialog_action_area1 = GTK_DIALOG (CaptureSourceChooserDialog)->action_area; + gtk_widget_show (dialog_action_area1); + gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area1), GTK_BUTTONBOX_END); + + cancelbutton1 = gtk_button_new_from_stock ("gtk-cancel"); + gtk_widget_show (cancelbutton1); + gtk_dialog_add_action_widget (GTK_DIALOG (CaptureSourceChooserDialog), cancelbutton1, GTK_RESPONSE_CANCEL); + GTK_WIDGET_SET_FLAGS (cancelbutton1, GTK_CAN_DEFAULT); + + okbutton1 = gtk_button_new_from_stock ("gtk-ok"); + gtk_widget_show (okbutton1); + gtk_dialog_add_action_widget (GTK_DIALOG (CaptureSourceChooserDialog), okbutton1, GTK_RESPONSE_OK); + GTK_WIDGET_SET_FLAGS (okbutton1, GTK_CAN_DEFAULT); + + capture_source_auto_detection(GTK_ICON_VIEW(VideoDevicesIconview)); + + char *filename = 0; + *isV4L2Device = 0; + + if (gtk_dialog_run (GTK_DIALOG (CaptureSourceChooserDialog)) == GTK_RESPONSE_OK) { + int page = gtk_notebook_get_current_page(GTK_NOTEBOOK(Notebook)); + + if (page==1) { // Multimedia File + + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(FileChooser)); + + + } else if (page==0){ + GList * list = gtk_icon_view_get_selected_items(GTK_ICON_VIEW(VideoDevicesIconview)); + if (list){ + GtkTreePath *path =(GtkTreePath*) list->data; + gchar *location; + GtkTreeIter iter; + GtkTreeModel *model = gtk_icon_view_get_model(GTK_ICON_VIEW(VideoDevicesIconview)); + + gtk_tree_model_get_iter(model,&iter,path); + + gtk_tree_model_get(model,&iter,2,&location,-1); + + filename = location; + *isV4L2Device = 1; + + g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL); + g_list_free (list); + } + } + } + + gtk_widget_destroy(CaptureSourceChooserDialog); + + return filename; +} + +static void change_image_format(GtkComboBox *combobox,void **data){ + V4L2Capture *dev = (V4L2Capture*) data[0]; + GtkWidget* ResolutionComboBox = (GtkWidget*) data[1]; + + int* formats = v4l2CaptureGetImageFormatsList(dev); + int index = gtk_combo_box_get_active(GTK_COMBO_BOX(combobox)); + int i; + int target; + char buf[256]; + int res; + + target =formats[index]; + + //printf("Choosed %s\n",fourcc_name(formats[index])); + + //Clear all items in the combo box + GtkTreeModel *model = gtk_combo_box_get_model(GTK_COMBO_BOX(ResolutionComboBox)); + gtk_list_store_clear(GTK_LIST_STORE(model)); + + res = v4l2CaptureSetImageFormat(dev,target,0); + + if(!res){ + /* Resolutions List */ + + VidSize *resolutions; + v4l2CaptureQueryResolutionsList(dev,&resolutions); + + if (resolutions){ + i = 0; + while (resolutions[i].width!=0){ + + snprintf(buf,sizeof(buf),"%dx%d", + resolutions[i].width,resolutions[i].height); + gtk_combo_box_append_text(GTK_COMBO_BOX(ResolutionComboBox),buf); + i++; + } + gtk_combo_box_set_active(GTK_COMBO_BOX(ResolutionComboBox),0); + } + + g_free(resolutions); + } +} + +static V4L2Capture* real_video_tuning_dialog_run(V4L2Capture *dev, + int *format,int *width,int *height,int *channel,int *norm,int *fps){ + GtkWidget *VideoTuningDialog; + GtkWidget *dialog_vbox2; + GtkWidget *table8; + GtkWidget *label50; + GtkWidget *label51; + GtkWidget *DeviceNameEntry; + GtkWidget *LocationEntry; + GtkWidget *label55; + GtkWidget *TVNormComboBox; + GtkWidget *label54; + GtkWidget *label58; + GtkWidget *SourceComboBox; + GtkWidget *FormatComboBox; + GtkWidget *label53; + GtkObject *FPSSpinButton_adj; + GtkWidget *FPSSpinButton; + GtkWidget *label52; + GtkWidget *ResolutionComboBox; + GtkWidget *label60; + GtkWidget *DriverEntry; + GtkWidget *dialog_action_area2; + GtkWidget *CancelButton; + GtkWidget *OKButton; + + VideoTuningDialog = gtk_dialog_new (); + gtk_window_set_title (GTK_WINDOW (VideoTuningDialog), "Video Tuning"); + gtk_window_set_type_hint (GTK_WINDOW (VideoTuningDialog), GDK_WINDOW_TYPE_HINT_DIALOG); + + dialog_vbox2 = GTK_DIALOG (VideoTuningDialog)->vbox; + gtk_widget_show (dialog_vbox2); + + table8 = gtk_table_new (6, 4, FALSE); + gtk_widget_show (table8); + gtk_box_pack_start (GTK_BOX (dialog_vbox2), table8, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (table8), 5); + gtk_table_set_row_spacings (GTK_TABLE (table8), 3); + gtk_table_set_col_spacings (GTK_TABLE (table8), 3); + + label50 = gtk_label_new ("Device Name"); + gtk_widget_show (label50); + gtk_table_attach (GTK_TABLE (table8), label50, 0, 1, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_label_set_justify (GTK_LABEL (label50), GTK_JUSTIFY_RIGHT); + gtk_label_set_line_wrap (GTK_LABEL (label50), TRUE); + gtk_misc_set_alignment (GTK_MISC (label50), 0, 0.5); + + label51 = gtk_label_new ("Location"); + gtk_widget_show (label51); + gtk_table_attach (GTK_TABLE (table8), label51, 0, 1, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_label_set_justify (GTK_LABEL (label51), GTK_JUSTIFY_RIGHT); + gtk_misc_set_alignment (GTK_MISC (label51), 0, 0.5); + + DeviceNameEntry = gtk_entry_new (); + gtk_widget_show (DeviceNameEntry); + gtk_table_attach (GTK_TABLE (table8), DeviceNameEntry, 1, 4, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_editable_set_editable (GTK_EDITABLE (DeviceNameEntry), FALSE); + + LocationEntry = gtk_entry_new (); + gtk_widget_show (LocationEntry); + gtk_table_attach (GTK_TABLE (table8), LocationEntry, 1, 4, 1, 2, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_editable_set_editable (GTK_EDITABLE (LocationEntry), FALSE); + + label55 = gtk_label_new ("TV Norm"); + gtk_widget_show (label55); + gtk_table_attach (GTK_TABLE (table8), label55, 0, 1, 5, 6, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_misc_set_alignment (GTK_MISC (label55), 0, 0.5); + + TVNormComboBox = gtk_combo_box_new_text (); + gtk_widget_show (TVNormComboBox); + gtk_table_attach (GTK_TABLE (table8), TVNormComboBox, 1, 2, 5, 6, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_FILL), 0, 0); + //gtk_combo_box_append_text (GTK_COMBO_BOX (TVNormComboBox), ""); + + label54 = gtk_label_new ("Source"); + gtk_widget_show (label54); + gtk_table_attach (GTK_TABLE (table8), label54, 0, 1, 4, 5, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_misc_set_alignment (GTK_MISC (label54), 0, 0.5); + + label58 = gtk_label_new ("Format"); + gtk_widget_show (label58); + gtk_table_attach (GTK_TABLE (table8), label58, 0, 1, 3, 4, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_misc_set_alignment (GTK_MISC (label58), 0, 0.5); + + SourceComboBox = gtk_combo_box_new_text (); + gtk_widget_show (SourceComboBox); + gtk_table_attach (GTK_TABLE (table8), SourceComboBox, 1, 2, 4, 5, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_FILL), 0, 0); + //gtk_combo_box_append_text (GTK_COMBO_BOX (SourceComboBox), ""); + + FormatComboBox = gtk_combo_box_new_text (); + gtk_widget_show (FormatComboBox); + gtk_table_attach (GTK_TABLE (table8), FormatComboBox, 1, 2, 3, 4, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_FILL), 0, 0); + //gtk_combo_box_append_text (GTK_COMBO_BOX (FormatComboBox), ""); + + label53 = gtk_label_new ("FPS"); + gtk_widget_show (label53); + gtk_table_attach (GTK_TABLE (table8), label53, 2, 3, 4, 5, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_misc_set_alignment (GTK_MISC (label53), 0, 0.5); + + FPSSpinButton_adj = gtk_adjustment_new (1, 0, 30, 1, 10, 10); + FPSSpinButton = gtk_spin_button_new (GTK_ADJUSTMENT (FPSSpinButton_adj), 1, 0); + gtk_widget_show (FPSSpinButton); + gtk_table_attach (GTK_TABLE (table8), FPSSpinButton, 3, 4, 4, 5, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + label52 = gtk_label_new ("Resolution"); + gtk_widget_show (label52); + gtk_table_attach (GTK_TABLE (table8), label52, 2, 3, 3, 4, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_misc_set_alignment (GTK_MISC (label52), 0, 0.5); + + ResolutionComboBox = gtk_combo_box_new_text (); + gtk_widget_show (ResolutionComboBox); + gtk_table_attach (GTK_TABLE (table8), ResolutionComboBox, 3, 4, 3, 4, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_FILL), 0, 0); + //gtk_combo_box_append_text (GTK_COMBO_BOX (ResolutionComboBox), ""); + + label60 = gtk_label_new ("Driver"); + gtk_widget_show (label60); + gtk_table_attach (GTK_TABLE (table8), label60, 0, 1, 2, 3, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_misc_set_alignment (GTK_MISC (label60), 0, 0.5); + + DriverEntry = gtk_entry_new (); + gtk_widget_show (DriverEntry); + gtk_table_attach (GTK_TABLE (table8), DriverEntry, 1, 4, 2, 3, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_editable_set_editable (GTK_EDITABLE (DriverEntry), FALSE); + + dialog_action_area2 = GTK_DIALOG (VideoTuningDialog)->action_area; + gtk_widget_show (dialog_action_area2); + gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area2), GTK_BUTTONBOX_END); + + CancelButton = gtk_button_new_from_stock ("gtk-cancel"); + gtk_widget_show (CancelButton); + gtk_dialog_add_action_widget (GTK_DIALOG (VideoTuningDialog), CancelButton, GTK_RESPONSE_CANCEL); + GTK_WIDGET_SET_FLAGS (CancelButton, GTK_CAN_DEFAULT); + + OKButton = gtk_button_new_from_stock ("gtk-ok"); + gtk_widget_show (OKButton); + gtk_dialog_add_action_widget (GTK_DIALOG (VideoTuningDialog), OKButton, GTK_RESPONSE_OK); + GTK_WIDGET_SET_FLAGS (OKButton, GTK_CAN_DEFAULT); + + /* End of GUI construction */ + + char buf[255]; + void *data[10]; + + gtk_entry_set_text(GTK_ENTRY(DeviceNameEntry),v4l2CaputreGetName(dev)); + gtk_entry_set_text(GTK_ENTRY(LocationEntry),v4l2CaputreGetLocation(dev)); + gtk_entry_set_text(GTK_ENTRY(DriverEntry),v4l2CaptureGetDriver(dev)); + + /* FPS */ + + int numerator; + int denominator; +// int channel; +// int norm; +// int format; + int i; +// int fps; +// int height; +// int width; + char *location; + + *fps = (int) round(v4l2CaptureGetFPS(dev)); + if (*fps>0){ + double min,max; + gtk_spin_button_get_range (GTK_SPIN_BUTTON(FPSSpinButton),&min,&max); + + if (*fps > max) + gtk_spin_button_set_range (GTK_SPIN_BUTTON(FPSSpinButton),min,*fps); + + gtk_spin_button_set_value(GTK_SPIN_BUTTON(FPSSpinButton),*fps); + + } else { + gtk_widget_set_sensitive(FPSSpinButton,0); + } + + + char **list = v4l2CaptureGetChannelsList(dev); + + if (list){ + i=0; + while (list[i]!=0){ + gtk_combo_box_append_text(GTK_COMBO_BOX(SourceComboBox),list[i]); + i++; + } + + gtk_combo_box_set_active(GTK_COMBO_BOX(SourceComboBox),0); + } + + /* Norms List */ + list = v4l2CaptureGetNormsList(dev); + if (list){ + i = 0; + while (list[i]!=0){ + gtk_combo_box_append_text(GTK_COMBO_BOX(TVNormComboBox),list[i]); + i++; + } + gtk_combo_box_set_active(GTK_COMBO_BOX(TVNormComboBox),0); + } + /* End of Norms List */ + + /* Format List */ + int *formats = v4l2CaptureGetImageFormatsList(dev); + + if (formats){ + i = 0; + while (formats[i]!=0){ + const char *name = vidFourccToString(formats[i]); + if (!name){ + snprintf(buf,sizeof(buf),"%08x",formats[i]); + name = buf; + } + gtk_combo_box_append_text(GTK_COMBO_BOX(FormatComboBox),name); + i++; + } + data[0] = dev; + data[1] = ResolutionComboBox; + data[2] = 0; + + g_signal_connect(GTK_OBJECT(FormatComboBox),"changed", + GTK_SIGNAL_FUNC(change_image_format) ,data); + + gtk_combo_box_set_active(GTK_COMBO_BOX(FormatComboBox),0); + } + + if (gtk_dialog_run (GTK_DIALOG (VideoTuningDialog))!= GTK_RESPONSE_OK){ + v4l2CaptureRelease(&dev); + } else { + if (GTK_WIDGET_SENSITIVE(FPSSpinButton)){ + *fps = (int) round(gtk_spin_button_get_value(GTK_SPIN_BUTTON(FPSSpinButton))); + v4l2CaptureSetFPS(dev,*fps); + } + + i = gtk_combo_box_get_active(GTK_COMBO_BOX(FormatComboBox)); + if (i!=-1){ + int *f=v4l2CaptureGetImageFormatsList(dev); + *format = f[i]; + v4l2CaptureSetImageFormat(dev,*format,0); + } + + char *screen = gtk_combo_box_get_active_text(GTK_COMBO_BOX(ResolutionComboBox)); + sscanf(screen,"%dx%d",width,height); + VidSize size; + size.width = *width; + size.height = *height; + + v4l2CaptureSetResolution(dev,&size); + + *channel = gtk_combo_box_get_active(GTK_COMBO_BOX(SourceComboBox)); + + if (*channel!=-1) + v4l2CaptureSetChannel(dev,*channel); + + *norm = gtk_combo_box_get_active(GTK_COMBO_BOX(TVNormComboBox)); + + if (*norm!=-1) + v4l2CaptureSetNorm(dev,*norm); + + } + + gtk_widget_destroy(VideoTuningDialog); + + + return dev; +} +/** + * @param location - the location of video device + * @Return A pointer to newly created V4L2Capture structure. It must be released by v4l2CaptureRelease. + */ + +V4L2Capture *video_tuning_dialog_run(const char *location){ + V4L2Capture *dev; + int format,width,height,channel,norm,fps; + + dev = v4l2CaptureOpen(location); + if (dev) { + dev = real_video_tuning_dialog_run(dev,&format,&width,&height, + &channel,&norm,&fps); + } + + return dev; +} + +/** + * @param location - the location of video device + * @param format - Return the chosen image format + * @param width - Returh the chosen image width + * @param height - Return the chosen image height + * @param channel - Return the chosen channel + * @param norm - Return the chosen TVNorm + * @param fps - Return the chosen FPS if the device is supported. + * @Return A pointer to newly created V4L2Capture structure. It must be released by v4l2CaptureRelease. + */ + +V4L2Capture *video_tuning_dialog_run_with_props(const char *location, + int *format,int *width,int *height,int *channel,int *norm,int *fps){ + V4L2Capture *dev; + + dev = v4l2CaptureOpen(location); + if (dev) { + dev = real_video_tuning_dialog_run(dev,format,width,height, + channel,norm,fps); + } + + return dev; +} + diff --git a/HenocCameraDrv/HenocMouseDriver/opencv/ui.h b/HenocCameraDrv/HenocMouseDriver/opencv/ui.h new file mode 100755 index 0000000..e758c11 --- /dev/null +++ b/HenocCameraDrv/HenocMouseDriver/opencv/ui.h @@ -0,0 +1,31 @@ +#ifndef UI_H_ +#define UI_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* defined(__cplusplus) */ + +/// Launch a dialog to choose capture source. The source could be a video device or a local multimedia file. + +char *capture_source_chooser_run(int *isV4L2Device); + +/// Launch a dialog to tune video device parameters. + +V4L2Capture *video_tuning_dialog_run(const char *location); + +/// Launch a dialog to tune video device parameters and return the assigned setting through arguments + +V4L2Capture *video_tuning_dialog_run_with_props(const char *location, + int *format,int *width,int *height,int *channel,int *norm,int *fps); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* defined(__cplusplus) */ + + + + +#endif /*UI_H_*/ diff --git a/HenocCameraDrv/HenocMouseDriver/v4l2.h b/HenocCameraDrv/HenocMouseDriver/v4l2.h new file mode 100755 index 0000000..7a47643 --- /dev/null +++ b/HenocCameraDrv/HenocMouseDriver/v4l2.h @@ -0,0 +1,6 @@ +#ifndef V4L2_H_ +#define V4L2_H_ + +#include + +#endif /*V4L2_H_*/ diff --git a/HenocCameraDrv/HenocMouseDriver/yuv2rgb.h b/HenocCameraDrv/HenocMouseDriver/yuv2rgb.h new file mode 100755 index 0000000..c026645 --- /dev/null +++ b/HenocCameraDrv/HenocMouseDriver/yuv2rgb.h @@ -0,0 +1,11 @@ +#ifndef __YUV2RGB_H_ +#define __YUV2RGB_H_ + +#include + +int yuv420_to_rgb24(VidFrame *src,VidFrame *dest); +int yuv420_to_bgr24(VidFrame *src,VidFrame *dest); +int yuyv_to_rgb24(VidFrame *src,VidFrame *dest); +int yuyv_to_bgr24(VidFrame *src,VidFrame *dest); + +#endif diff --git a/HenocCameraDrv/generaHomografia b/HenocCameraDrv/generaHomografia new file mode 100755 index 0000000000000000000000000000000000000000..6baa87e4b4db517dadf38ae40af9ba94fce10d97 GIT binary patch literal 9197 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 ZnvqB9xo_`tAN1$^g}}^T0L*^E|39IvYjOYp literal 0 HcmV?d00001 diff --git a/HenocCameraDrv/uvccapture-0.4/ChangeLog b/HenocCameraDrv/uvccapture-0.4/ChangeLog new file mode 100755 index 0000000..733c04e --- /dev/null +++ b/HenocCameraDrv/uvccapture-0.4/ChangeLog @@ -0,0 +1,21 @@ +-- uvccapture 0.4 21.03.2006 -- +Lots of changes: +* Added install target in the makefile to install into /usr/local/bin +* Added signal capturing (CTRL-C, etc) to gracefully exit when in wait loop +* Added the ability to specify a command to run after each image capture, + command is run with the output picture as an argument. command can be run + blocking, or non-blocking. +* Added support for multiple verbisity levels (add more -v's) +* Minor output spelling corrections +* Added output of current bright/contrast/saturation/gain on startup. +All these thanks to Salvador Gonzalez +* YVYU Capture Mode - now requires jpeglib, activated for images sizes >960x720 + allows adjustment of JPEG compression quality (-q) or manually activated (-m) +YUYV -> JPEG code thanks to Philip Heron + +-- uvccapture 0.3 19.02.2006 -- +Removed the JPEG decoding files (util.*) and the calls to convert-frame during +the capture of images. This decoding was only important for SDL display, which +is already gone. Peak CPU usage has gone from ~30% to 0.6%. + +Thanks to Philip Heron for the suggestion. \ No newline at end of file diff --git a/HenocCameraDrv/uvccapture-0.4/Makefile b/HenocCameraDrv/uvccapture-0.4/Makefile new file mode 100755 index 0000000..f9dc6ec --- /dev/null +++ b/HenocCameraDrv/uvccapture-0.4/Makefile @@ -0,0 +1,27 @@ +CC=gcc +CPP=g++ +APP_BINARY=uvccapture +VERSION = 0.4 +PREFIX=/usr/local/bin + +WARNINGS = -Wall + + +CFLAGS = -std=gnu99 -O2 -DLINUX -DVERSION=\"$(VERSION)\" $(WARNINGS) +CPPFLAGS = $(CFLAGS) + +OBJECTS= uvccapture.o v4l2uvc.o + + +all: uvccapture + +clean: + @echo "Cleaning up directory." + rm -f *.a *.o $(APP_BINARY) core *~ log errlog + +install: + install $(APP_BINARY) $(PREFIX) + +# Applications: +uvccapture: $(OBJECTS) + $(CC) $(OBJECTS) $(XPM_LIB) $(MATH_LIB) -ljpeg -o $(APP_BINARY) diff --git a/HenocCameraDrv/uvccapture-0.4/copying b/HenocCameraDrv/uvccapture-0.4/copying new file mode 100755 index 0000000..d60c31a --- /dev/null +++ b/HenocCameraDrv/uvccapture-0.4/copying @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/HenocCameraDrv/uvccapture-0.4/readme b/HenocCameraDrv/uvccapture-0.4/readme new file mode 100755 index 0000000..ca41361 --- /dev/null +++ b/HenocCameraDrv/uvccapture-0.4/readme @@ -0,0 +1,30 @@ +uvccapture: USB UVC Video Class Snapshot Software +Newest versions always available at http://staticwave.ca/source/uvccapture + +This software is based of the package luvcview: +Copyright (C) 2005 2006 Laurent Pinchart && Michel Xhaard + +Modifications and updates: +Copyright (C) 2006 Gabriel A. Devenyi + +This program communicates via the USB UVC Video Class driver for V4L2, available +at http://linux-uvc.berlios.de/. + +The purpose of this software is to capture an image from a USB webcam at a +specified interval, and save it to a JPEG file, no other formats are supported, +imagemagick can handle anything else you might need. + +Right now this software is really a hack, since still image support is not yet +available in the UVC driver. The program continually polls the UVC driver in +MJPEG mode, and at a specified interval writes a JPEG header and a single frame +to file, creating a JPEG image. Because of this continuous polling, this program +has high CPU usage. Input checking is pretty trusting, so call it with the right +parameters. + +In order for this software to work, whatever device you pass to -d you will need +to have read/write rights to, please see appropriate /dev documentation for how +to do this. + +This software depends on jpeglib(present on almost all systems) + +For usage information, run uvccapture -h \ No newline at end of file diff --git a/HenocCameraDrv/uvccapture-0.4/todo b/HenocCameraDrv/uvccapture-0.4/todo new file mode 100755 index 0000000..1569d29 --- /dev/null +++ b/HenocCameraDrv/uvccapture-0.4/todo @@ -0,0 +1 @@ +*Add support for PAN and TILT \ No newline at end of file diff --git a/HenocCameraDrv/uvccapture-0.4/uvccapture b/HenocCameraDrv/uvccapture-0.4/uvccapture new file mode 100755 index 0000000000000000000000000000000000000000..4cf16a19e98b31dce6aeebf6df4d61d7a4cac394 GIT binary patch literal 22712 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 W$8i{E7owgO@fB^JSjfY&wEqFNPsaxU literal 0 HcmV?d00001 diff --git a/HenocCameraDrv/uvccapture-0.4/uvccapture.c b/HenocCameraDrv/uvccapture-0.4/uvccapture.c new file mode 100755 index 0000000..4ce2e3b --- /dev/null +++ b/HenocCameraDrv/uvccapture-0.4/uvccapture.c @@ -0,0 +1,398 @@ + +/******************************************************************************* +# uvccapture: USB UVC Video Class Snapshot Software # +#This package work with the Logitech UVC based webcams with the mjpeg feature # +#. # +# Orginally Copyright (C) 2005 2006 Laurent Pinchart && Michel Xhaard # +# Modifications Copyright (C) 2006 Gabriel A. Devenyi # +# # +# This program is free software; you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation; either version 2 of the License, or # +# (at your option) any later version. # +# # +# This program 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 General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +# # +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "v4l2uvc.h" + +static const char version[] = VERSION; +int run = 1; + +void +sigcatch (int sig) +{ + fprintf (stderr, "Exiting...\n"); + run = 0; +} + +void +usage (void) +{ + fprintf (stderr, "uvccapture version %s\n", version); + fprintf (stderr, "Usage is: uvccapture [options]\n"); + fprintf (stderr, "Options:\n"); + fprintf (stderr, "-v\t\tVerbose\n"); + fprintf (stderr, "-o\tOutput filename(default: snap.jpg)\n"); + fprintf (stderr, "-d\tV4L2 Device(default: /dev/video0)\n"); + fprintf (stderr, + "-x\tImage Width(must be supported by device)(>960 activates YUYV capture)\n"); + fprintf (stderr, + "-y\tImage Height(must be supported by device)(>720 activates YUYV capture)\n"); + fprintf (stderr, + "-c\tCommand to run after each image capture(executed as )\n"); + fprintf (stderr, + "-t\tTake continuous shots with seconds between them (0 for single shot)\n"); + fprintf (stderr, + "-q\tJPEG Quality Compression Level (activates YUYV capture)\n"); + fprintf (stderr, "-r\t\tUse read instead of mmap for image capture\n"); + fprintf (stderr, + "-w\t\tWait for capture command to finish before starting next capture\n"); + fprintf (stderr, "-m\t\tToggles capture mode to YUYV capture\n"); + fprintf (stderr, "Camera Settings:\n"); + fprintf (stderr, "-B\tBrightness\n"); + fprintf (stderr, "-C\tContrast\n"); + fprintf (stderr, "-S\tSaturation\n"); + fprintf (stderr, "-G\tGain\n"); + exit (8); +} + +int +spawn (char *argv[], int wait, int verbose) +{ + pid_t pid; + int rv; + + switch (pid = fork ()) { + case -1: + return -1; + case 0: + // CHILD + execvp (argv[0], argv); + fprintf (stderr, "Error executing command '%s'\n", argv[0]); + exit (1); + default: + // PARENT + if (wait == 1) { + if (verbose >= 1) + fprintf (stderr, "Waiting for command to finish..."); + waitpid (pid, &rv, 0); + if (verbose >= 1) + fprintf (stderr, "\n"); + } else { + // Clean zombies + waitpid (-1, &rv, WNOHANG); + rv = 0; + } + break; + } + + return rv; +} + +int +compress_yuyv_to_jpeg (struct vdIn *vd, FILE * file, int quality) +{ + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + JSAMPROW row_pointer[1]; + unsigned char *line_buffer, *yuyv; + int z; + + line_buffer = calloc (vd->width * 3, 1); + yuyv = vd->framebuffer; + + cinfo.err = jpeg_std_error (&jerr); + jpeg_create_compress (&cinfo); + jpeg_stdio_dest (&cinfo, file); + + cinfo.image_width = vd->width; + cinfo.image_height = vd->height; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + + jpeg_set_defaults (&cinfo); + jpeg_set_quality (&cinfo, quality, TRUE); + + jpeg_start_compress (&cinfo, TRUE); + + z = 0; + while (cinfo.next_scanline < cinfo.image_height) { + int x; + unsigned char *ptr = line_buffer; + + for (x = 0; x < vd->width; x++) { + int r, g, b; + int y, u, v; + + if (!z) + y = yuyv[0] << 8; + else + y = yuyv[2] << 8; + u = yuyv[1] - 128; + v = yuyv[3] - 128; + + r = (y + (359 * v)) >> 8; + g = (y - (88 * u) - (183 * v)) >> 8; + b = (y + (454 * u)) >> 8; + + *(ptr++) = (r > 255) ? 255 : ((r < 0) ? 0 : r); + *(ptr++) = (g > 255) ? 255 : ((g < 0) ? 0 : g); + *(ptr++) = (b > 255) ? 255 : ((b < 0) ? 0 : b); + + if (z++) { + z = 0; + yuyv += 4; + } + } + + row_pointer[0] = line_buffer; + jpeg_write_scanlines (&cinfo, row_pointer, 1); + } + + jpeg_finish_compress (&cinfo); + jpeg_destroy_compress (&cinfo); + + free (line_buffer); + + return (0); +} + +int +main (int argc, char *argv[]) +{ + char *videodevice = "/dev/video0"; + char *outputfile = "snap.jpg"; + char *post_capture_command[3]; + int format = V4L2_PIX_FMT_MJPEG; + int grabmethod = 1; + int width = 320; + int height = 240; + int brightness = 0, contrast = 0, saturation = 0, gain = 0; + int verbose = 0; + int delay = 0; + int quality = 95; + int post_capture_command_wait = 0; + time_t ref_time; + struct vdIn *videoIn; + FILE *file; + + (void) signal (SIGINT, sigcatch); + (void) signal (SIGQUIT, sigcatch); + (void) signal (SIGKILL, sigcatch); + (void) signal (SIGTERM, sigcatch); + (void) signal (SIGABRT, sigcatch); + (void) signal (SIGTRAP, sigcatch); + + // set post_capture_command to default values + post_capture_command[0] = NULL; + post_capture_command[1] = NULL; + post_capture_command[2] = NULL; + + //Options Parsing (FIXME) + while ((argc > 1) && (argv[1][0] == '-')) { + switch (argv[1][1]) { + case 'v': + verbose++; + break; + + case 'o': + outputfile = &argv[1][2]; + break; + + case 'd': + videodevice = &argv[1][2]; + break; + + case 'x': + width = atoi (&argv[1][2]); + break; + + case 'y': + height = atoi (&argv[1][2]); + break; + + case 'r': + grabmethod = 0; + break; + + case 'm': + format = V4L2_PIX_FMT_YUYV; + break; + + case 't': + delay = atoi (&argv[1][2]); + break; + + case 'c': + post_capture_command[0] = &argv[1][2]; + break; + + case 'w': + post_capture_command_wait = 1; + break; + + case 'B': + brightness = atoi (&argv[1][2]); + break; + + case 'C': + contrast = atoi (&argv[1][2]); + break; + + case 'S': + saturation = atoi (&argv[1][2]); + break; + + case 'G': + gain = atoi (&argv[1][2]); + break; + + case 'q': + quality = atoi (&argv[1][2]); + break; + + case 'h': + usage (); + break; + + default: + fprintf (stderr, "Unknown option %s \n", argv[1]); + usage (); + } + ++argv; + --argc; + } + + if ((width > 960) || (height > 720) || (quality != 95)) + format = V4L2_PIX_FMT_YUYV; + + if (post_capture_command[0]) + post_capture_command[1] = outputfile; + + if (verbose >= 1) { + fprintf (stderr, "Using videodevice: %s\n", videodevice); + fprintf (stderr, "Saving images to: %s\n", outputfile); + fprintf (stderr, "Image size: %dx%d\n", width, height); + fprintf (stderr, "Taking snapshot every %d seconds\n", delay); + if (grabmethod == 1) + fprintf (stderr, "Taking images using mmap\n"); + else + fprintf (stderr, "Taking images using read\n"); + if (post_capture_command[0]) + fprintf (stderr, "Executing '%s' after each image capture\n", + post_capture_command[0]); + } + videoIn = (struct vdIn *) calloc (1, sizeof (struct vdIn)); + if (init_videoIn + (videoIn, (char *) videodevice, width, height, format, grabmethod) < 0) + exit (1); + + //Reset all camera controls + if (verbose >= 1) + fprintf (stderr, "Resetting camera settings\n"); + v4l2ResetControl (videoIn, V4L2_CID_BRIGHTNESS); + v4l2ResetControl (videoIn, V4L2_CID_CONTRAST); + v4l2ResetControl (videoIn, V4L2_CID_SATURATION); + v4l2ResetControl (videoIn, V4L2_CID_GAIN); + + //Setup Camera Parameters + if (brightness != 0) { + if (verbose >= 1) + fprintf (stderr, "Setting camera brightness to %d\n", brightness); + v4l2SetControl (videoIn, V4L2_CID_BRIGHTNESS, brightness); + } else if (verbose >= 1) { + fprintf (stderr, "Camera brightness level is %d\n", + v4l2GetControl (videoIn, V4L2_CID_BRIGHTNESS)); + } + if (contrast != 0) { + if (verbose >= 1) + fprintf (stderr, "Setting camera contrast to %d\n", contrast); + v4l2SetControl (videoIn, V4L2_CID_CONTRAST, contrast); + } else if (verbose >= 1) { + fprintf (stderr, "Camera contrast level is %d\n", + v4l2GetControl (videoIn, V4L2_CID_CONTRAST)); + } + if (saturation != 0) { + if (verbose >= 1) + fprintf (stderr, "Setting camera saturation to %d\n", saturation); + v4l2SetControl (videoIn, V4L2_CID_SATURATION, saturation); + } else if (verbose >= 1) { + fprintf (stderr, "Camera saturation level is %d\n", + v4l2GetControl (videoIn, V4L2_CID_SATURATION)); + } + if (gain != 0) { + if (verbose >= 1) + fprintf (stderr, "Setting camera gain to %d\n", gain); + v4l2SetControl (videoIn, V4L2_CID_GAIN, gain); + } else if (verbose >= 1) { + fprintf (stderr, "Camera gain level is %d\n", + v4l2GetControl (videoIn, V4L2_CID_GAIN)); + } + ref_time = time (NULL); + + while (run) { + if (verbose >= 2) + fprintf (stderr, "Grabbing frame\n"); + if (uvcGrab (videoIn) < 0) { + fprintf (stderr, "Error grabbing\n"); + close_v4l2 (videoIn); + free (videoIn); + exit (1); + } + + if ((difftime (time (NULL), ref_time) > delay) || delay == 0) { + if (verbose >= 1) + fprintf (stderr, "Saving image to: %s\n", outputfile); + file = fopen (outputfile, "wb"); + if (file != NULL) { + switch (videoIn->formatIn) { + case V4L2_PIX_FMT_YUYV: + compress_yuyv_to_jpeg (videoIn, file, quality); + break; + default: + fwrite (videoIn->tmpbuffer, videoIn->buf.bytesused + DHT_SIZE, 1, + file); + break; + } + fclose (file); + videoIn->getPict = 0; + } + if (post_capture_command[0]) + if (verbose >= 1) + fprintf (stderr, "Executing '%s %s'\n", post_capture_command[0], + post_capture_command[1]); + if (spawn (post_capture_command, post_capture_command_wait, verbose)) { + fprintf (stderr, "Command exited with error\n"); + close_v4l2 (videoIn); + free (videoIn); + exit (1); + } + + ref_time = time (NULL); + } + if (delay == 0) + break; + } + close_v4l2 (videoIn); + free (videoIn); + + return 0; +} diff --git a/HenocCameraDrv/uvccapture-0.4/v4l2uvc.c b/HenocCameraDrv/uvccapture-0.4/v4l2uvc.c new file mode 100755 index 0000000..8f493b1 --- /dev/null +++ b/HenocCameraDrv/uvccapture-0.4/v4l2uvc.c @@ -0,0 +1,591 @@ + +/******************************************************************************* +# uvccapture: USB UVC Video Class Snapshot Software # +#This package work with the Logitech UVC based webcams with the mjpeg feature # +#. # +# Orginally Copyright (C) 2005 2006 Laurent Pinchart && Michel Xhaard # +# Modifications Copyright (C) 2006 Gabriel A. Devenyi # +# # +# This program is free software; you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation; either version 2 of the License, or # +# (at your option) any later version. # +# # +# This program 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 General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +# # +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "v4l2uvc.h" + +static int debug = 0; + +static unsigned char dht_data[DHT_SIZE] = { + 0xff, 0xc4, 0x01, 0xa2, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x01, 0x00, 0x03, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, + 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, + 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, + 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, + 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, + 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, + 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, + 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, + 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, + 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, + 0xfa, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, + 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, + 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, + 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, + 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, + 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, + 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, + 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, + 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, + 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa +}; + +static int init_v4l2 (struct vdIn *vd); + +int +init_videoIn (struct vdIn *vd, char *device, int width, int height, + int format, int grabmethod) +{ + + if (vd == NULL || device == NULL) + return -1; + if (width == 0 || height == 0) + return -1; + if (grabmethod < 0 || grabmethod > 1) + grabmethod = 1; //mmap by default; + vd->videodevice = NULL; + vd->status = NULL; + vd->pictName = NULL; + vd->videodevice = (char *) calloc (1, 16 * sizeof (char)); + vd->status = (char *) calloc (1, 100 * sizeof (char)); + vd->pictName = (char *) calloc (1, 80 * sizeof (char)); + snprintf (vd->videodevice, 12, "%s", device); + vd->toggleAvi = 0; + vd->getPict = 0; + vd->signalquit = 1; + vd->width = width; + vd->height = height; + vd->formatIn = format; + vd->grabmethod = grabmethod; + if (init_v4l2 (vd) < 0) { + fprintf (stderr, " Init v4L2 failed !! exit fatal \n"); + goto error;; + } + /* alloc a temp buffer to reconstruct the pict */ + vd->framesizeIn = (vd->width * vd->height << 1); + switch (vd->formatIn) { + case V4L2_PIX_FMT_MJPEG: + vd->tmpbuffer = (unsigned char *) calloc (1, (size_t) vd->framesizeIn); + if (!vd->tmpbuffer) + goto error; + vd->framebuffer = + (unsigned char *) calloc (1, (size_t) vd->width * (vd->height + 8) * 2); + break; + case V4L2_PIX_FMT_YUYV: + vd->framebuffer = (unsigned char *) calloc (1, (size_t) vd->framesizeIn); + break; + default: + fprintf (stderr, " should never arrive exit fatal !!\n"); + goto error; + break; + } + if (!vd->framebuffer) + goto error; + return 0; +error: + free (vd->videodevice); + free (vd->status); + free (vd->pictName); + close (vd->fd); + return -1; +} + +static int +init_v4l2 (struct vdIn *vd) +{ + int i; + int ret = 0; + + if ((vd->fd = open (vd->videodevice, O_RDWR)) == -1) { + perror ("ERROR opening V4L interface \n"); + exit (1); + } + memset (&vd->cap, 0, sizeof (struct v4l2_capability)); + ret = ioctl (vd->fd, VIDIOC_QUERYCAP, &vd->cap); + if (ret < 0) { + fprintf (stderr, "Error opening device %s: unable to query device.\n", + vd->videodevice); + goto fatal; + } + + if ((vd->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) { + fprintf (stderr, + "Error opening device %s: video capture not supported.\n", + vd->videodevice); + goto fatal;; + } + if (vd->grabmethod) { + if (!(vd->cap.capabilities & V4L2_CAP_STREAMING)) { + fprintf (stderr, "%s does not support streaming i/o\n", + vd->videodevice); + goto fatal; + } + } else { + if (!(vd->cap.capabilities & V4L2_CAP_READWRITE)) { + fprintf (stderr, "%s does not support read i/o\n", vd->videodevice); + goto fatal; + } + } + /* set format in */ + memset (&vd->fmt, 0, sizeof (struct v4l2_format)); + vd->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vd->fmt.fmt.pix.width = vd->width; + vd->fmt.fmt.pix.height = vd->height; + vd->fmt.fmt.pix.pixelformat = vd->formatIn; + vd->fmt.fmt.pix.field = V4L2_FIELD_ANY; + ret = ioctl (vd->fd, VIDIOC_S_FMT, &vd->fmt); + if (ret < 0) { + fprintf (stderr, "Unable to set format: %d.\n", errno); + goto fatal; + } + if ((vd->fmt.fmt.pix.width != vd->width) || + (vd->fmt.fmt.pix.height != vd->height)) { + fprintf (stderr, " format asked unavailable get width %d height %d \n", + vd->fmt.fmt.pix.width, vd->fmt.fmt.pix.height); + vd->width = vd->fmt.fmt.pix.width; + vd->height = vd->fmt.fmt.pix.height; + /* look the format is not part of the deal ??? */ + //vd->formatIn = vd->fmt.fmt.pix.pixelformat; + } + /* request buffers */ + memset (&vd->rb, 0, sizeof (struct v4l2_requestbuffers)); + vd->rb.count = NB_BUFFER; + vd->rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vd->rb.memory = V4L2_MEMORY_MMAP; + + ret = ioctl (vd->fd, VIDIOC_REQBUFS, &vd->rb); + if (ret < 0) { + fprintf (stderr, "Unable to allocate buffers: %d.\n", errno); + goto fatal; + } + /* map the buffers */ + for (i = 0; i < NB_BUFFER; i++) { + memset (&vd->buf, 0, sizeof (struct v4l2_buffer)); + vd->buf.index = i; + vd->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vd->buf.memory = V4L2_MEMORY_MMAP; + ret = ioctl (vd->fd, VIDIOC_QUERYBUF, &vd->buf); + if (ret < 0) { + fprintf (stderr, "Unable to query buffer (%d).\n", errno); + goto fatal; + } + if (debug) + fprintf (stderr, "length: %u offset: %u\n", vd->buf.length, + vd->buf.m.offset); + vd->mem[i] = mmap (0 /* start anywhere */ , + vd->buf.length, PROT_READ, MAP_SHARED, vd->fd, + vd->buf.m.offset); + if (vd->mem[i] == MAP_FAILED) { + fprintf (stderr, "Unable to map buffer (%d)\n", errno); + goto fatal; + } + if (debug) + fprintf (stderr, "Buffer mapped at address %p.\n", vd->mem[i]); + } + /* Queue the buffers. */ + for (i = 0; i < NB_BUFFER; ++i) { + memset (&vd->buf, 0, sizeof (struct v4l2_buffer)); + vd->buf.index = i; + vd->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vd->buf.memory = V4L2_MEMORY_MMAP; + ret = ioctl (vd->fd, VIDIOC_QBUF, &vd->buf); + if (ret < 0) { + fprintf (stderr, "Unable to queue buffer (%d).\n", errno); + goto fatal;; + } + } + return 0; +fatal: + return -1; + +} + +static int +video_enable (struct vdIn *vd) +{ + int type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + int ret; + + ret = ioctl (vd->fd, VIDIOC_STREAMON, &type); + if (ret < 0) { + fprintf (stderr, "Unable to %s capture: %d.\n", "start", errno); + return ret; + } + vd->isstreaming = 1; + return 0; +} + +static int +video_disable (struct vdIn *vd) +{ + int type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + int ret; + + ret = ioctl (vd->fd, VIDIOC_STREAMOFF, &type); + if (ret < 0) { + fprintf (stderr, "Unable to %s capture: %d.\n", "stop", errno); + return ret; + } + vd->isstreaming = 0; + return 0; +} + +int +uvcGrab (struct vdIn *vd) +{ +#define HEADERFRAME1 0xaf + int ret; + + if (!vd->isstreaming) + if (video_enable (vd)) + goto err; + memset (&vd->buf, 0, sizeof (struct v4l2_buffer)); + vd->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vd->buf.memory = V4L2_MEMORY_MMAP; + ret = ioctl (vd->fd, VIDIOC_DQBUF, &vd->buf); + if (ret < 0) { + fprintf (stderr, "Unable to dequeue buffer (%d).\n", errno); + goto err; + } + switch (vd->formatIn) { + case V4L2_PIX_FMT_MJPEG: + + memcpy (vd->tmpbuffer, vd->mem[vd->buf.index], HEADERFRAME1); + memcpy (vd->tmpbuffer + HEADERFRAME1, dht_data, DHT_SIZE); + memcpy (vd->tmpbuffer + HEADERFRAME1 + DHT_SIZE, + vd->mem[vd->buf.index] + HEADERFRAME1, + (vd->buf.bytesused - HEADERFRAME1)); + if (debug) + fprintf (stderr, "bytes in used %d \n", vd->buf.bytesused); + break; + case V4L2_PIX_FMT_YUYV: + if (vd->buf.bytesused > vd->framesizeIn) + memcpy (vd->framebuffer, vd->mem[vd->buf.index], + (size_t) vd->framesizeIn); + else + memcpy (vd->framebuffer, vd->mem[vd->buf.index], + (size_t) vd->buf.bytesused); + break; + default: + goto err; + break; + } + ret = ioctl (vd->fd, VIDIOC_QBUF, &vd->buf); + if (ret < 0) { + fprintf (stderr, "Unable to requeue buffer (%d).\n", errno); + goto err; + } + + return 0; +err: + vd->signalquit = 0; + return -1; +} + +int +close_v4l2 (struct vdIn *vd) +{ + int i; + + if (vd->isstreaming) + video_disable (vd); + + /* If the memory maps are not released the device will remain opened even + after a call to close(); */ + for (i = 0; i < NB_BUFFER; i++) { + munmap (vd->mem[i], vd->buf.length); + } + + if (vd->tmpbuffer) + free (vd->tmpbuffer); + vd->tmpbuffer = NULL; + free (vd->framebuffer); + vd->framebuffer = NULL; + free (vd->videodevice); + free (vd->status); + free (vd->pictName); + vd->videodevice = NULL; + vd->status = NULL; + vd->pictName = NULL; + close (vd->fd); + return 0; +} + +/* return >= 0 ok otherwhise -1 */ +static int +isv4l2Control (struct vdIn *vd, int control, struct v4l2_queryctrl *queryctrl) +{ + int err = 0; + + queryctrl->id = control; + if ((err = ioctl (vd->fd, VIDIOC_QUERYCTRL, queryctrl)) < 0) { + fprintf (stderr, "ioctl querycontrol error %d \n", errno); + } else if (queryctrl->flags & V4L2_CTRL_FLAG_DISABLED) { + fprintf (stderr, "control %s disabled \n", (char *) queryctrl->name); + } else if (queryctrl->flags & V4L2_CTRL_TYPE_BOOLEAN) { + return 1; + } else if (queryctrl->type & V4L2_CTRL_TYPE_INTEGER) { + return 0; + } else { + fprintf (stderr, "contol %s unsupported \n", (char *) queryctrl->name); + } + return -1; +} + +int +v4l2GetControl (struct vdIn *vd, int control) +{ + struct v4l2_queryctrl queryctrl; + struct v4l2_control control_s; + int err; + + if (isv4l2Control (vd, control, &queryctrl) < 0) + return -1; + control_s.id = control; + if ((err = ioctl (vd->fd, VIDIOC_G_CTRL, &control_s)) < 0) { + fprintf (stderr, "ioctl get control error\n"); + return -1; + } + return control_s.value; +} + +int +v4l2SetControl (struct vdIn *vd, int control, int value) +{ + struct v4l2_control control_s; + struct v4l2_queryctrl queryctrl; + int min, max, step, val_def; + int err; + + if (isv4l2Control (vd, control, &queryctrl) < 0) + return -1; + min = queryctrl.minimum; + max = queryctrl.maximum; + step = queryctrl.step; + val_def = queryctrl.default_value; + if ((value >= min) && (value <= max)) { + control_s.id = control; + control_s.value = value; + if ((err = ioctl (vd->fd, VIDIOC_S_CTRL, &control_s)) < 0) { + fprintf (stderr, "ioctl set control error\n"); + return -1; + } + } + return 0; +} + +int +v4l2UpControl (struct vdIn *vd, int control) +{ + struct v4l2_control control_s; + struct v4l2_queryctrl queryctrl; + int min, max, current, step, val_def; + int err; + + if (isv4l2Control (vd, control, &queryctrl) < 0) + return -1; + min = queryctrl.minimum; + max = queryctrl.maximum; + step = queryctrl.step; + val_def = queryctrl.default_value; + current = v4l2GetControl (vd, control); + current += step; + if (current <= max) { + control_s.id = control; + control_s.value = current; + if ((err = ioctl (vd->fd, VIDIOC_S_CTRL, &control_s)) < 0) { + fprintf (stderr, "ioctl set control error\n"); + return -1; + } + } + return control_s.value; +} + +int +v4l2DownControl (struct vdIn *vd, int control) +{ + struct v4l2_control control_s; + struct v4l2_queryctrl queryctrl; + int min, max, current, step, val_def; + int err; + + if (isv4l2Control (vd, control, &queryctrl) < 0) + return -1; + min = queryctrl.minimum; + max = queryctrl.maximum; + step = queryctrl.step; + val_def = queryctrl.default_value; + current = v4l2GetControl (vd, control); + current -= step; + if (current >= min) { + control_s.id = control; + control_s.value = current; + if ((err = ioctl (vd->fd, VIDIOC_S_CTRL, &control_s)) < 0) { + fprintf (stderr, "ioctl set control error\n"); + return -1; + } + } + return control_s.value; +} + +int +v4l2ToggleControl (struct vdIn *vd, int control) +{ + struct v4l2_control control_s; + struct v4l2_queryctrl queryctrl; + int current; + int err; + + if (isv4l2Control (vd, control, &queryctrl) != 1) + return -1; + current = v4l2GetControl (vd, control); + control_s.id = control; + control_s.value = !current; + if ((err = ioctl (vd->fd, VIDIOC_S_CTRL, &control_s)) < 0) { + fprintf (stderr, "ioctl toggle control error\n"); + return -1; + } + return control_s.value; +} + +int +v4l2ResetControl (struct vdIn *vd, int control) +{ + struct v4l2_control control_s; + struct v4l2_queryctrl queryctrl; + int val_def; + int err; + + if (isv4l2Control (vd, control, &queryctrl) < 0) + return -1; + val_def = queryctrl.default_value; + control_s.id = control; + control_s.value = val_def; + if ((err = ioctl (vd->fd, VIDIOC_S_CTRL, &control_s)) < 0) { + fprintf (stderr, "ioctl reset control error\n"); + return -1; + } + + return 0; +} + +int +v4l2ResetPanTilt (struct vdIn *vd, int pantilt) +{ + int control = V4L2_CID_PANTILT_RESET; + struct v4l2_control control_s; + struct v4l2_queryctrl queryctrl; + unsigned char val; + int err; + + if (isv4l2Control (vd, control, &queryctrl) < 0) + return -1; + val = (unsigned char) pantilt; + control_s.id = control; + control_s.value = val; + if ((err = ioctl (vd->fd, VIDIOC_S_CTRL, &control_s)) < 0) { + fprintf (stderr, "ioctl reset Pan control error\n"); + return -1; + } + + return 0; +} +union pantilt { + struct { + short pan; + short tilt; + } s16; + int value; +} pantilt; + +int +v4L2UpDownPan (struct vdIn *vd, short inc) +{ + int control = V4L2_CID_PANTILT_RELATIVE; + struct v4l2_control control_s; + struct v4l2_queryctrl queryctrl; + int err; + + union pantilt pan; + + control_s.id = control; + if (isv4l2Control (vd, control, &queryctrl) < 0) + return -1; + + pan.s16.pan = inc; + pan.s16.tilt = 0; + + control_s.value = pan.value; + if ((err = ioctl (vd->fd, VIDIOC_S_CTRL, &control_s)) < 0) { + fprintf (stderr, "ioctl pan updown control error\n"); + return -1; + } + return 0; +} + +int +v4L2UpDownTilt (struct vdIn *vd, short inc) +{ + int control = V4L2_CID_PANTILT_RELATIVE; + struct v4l2_control control_s; + struct v4l2_queryctrl queryctrl; + int err; + union pantilt pan; + + control_s.id = control; + if (isv4l2Control (vd, control, &queryctrl) < 0) + return -1; + + pan.s16.pan = 0; + pan.s16.tilt = inc; + + control_s.value = pan.value; + if ((err = ioctl (vd->fd, VIDIOC_S_CTRL, &control_s)) < 0) { + fprintf (stderr, "ioctl tiltupdown control error\n"); + return -1; + } + return 0; +} diff --git a/HenocCameraDrv/uvccapture-0.4/v4l2uvc.h b/HenocCameraDrv/uvccapture-0.4/v4l2uvc.h new file mode 100755 index 0000000..b3bacad --- /dev/null +++ b/HenocCameraDrv/uvccapture-0.4/v4l2uvc.h @@ -0,0 +1,78 @@ + +/******************************************************************************* +# uvccapture: USB UVC Video Class Snapshot Software # +#This package work with the Logitech UVC based webcams with the mjpeg feature # +# # +# Orginally Copyright (C) 2005 2006 Laurent Pinchart && Michel Xhaard # +# Modifications Copyright (C) 2006 Gabriel A. Devenyi # +# # +# This program is free software; you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation; either version 2 of the License, or # +# (at your option) any later version. # +# # +# This program 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 General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +# # +*******************************************************************************/ + +#define NB_BUFFER 16 +#define DHT_SIZE 420 + +#define V4L2_CID_BACKLIGHT_COMPENSATION (V4L2_CID_PRIVATE_BASE+0) +#define V4L2_CID_POWER_LINE_FREQUENCY (V4L2_CID_PRIVATE_BASE+1) +#define V4L2_CID_SHARPNESS (V4L2_CID_PRIVATE_BASE+2) +#define V4L2_CID_HUE_AUTO (V4L2_CID_PRIVATE_BASE+3) +#define V4L2_CID_FOCUS_AUTO (V4L2_CID_PRIVATE_BASE+4) +#define V4L2_CID_FOCUS_ABSOLUTE (V4L2_CID_PRIVATE_BASE+5) +#define V4L2_CID_FOCUS_RELATIVE (V4L2_CID_PRIVATE_BASE+6) + +#define V4L2_CID_PANTILT_RELATIVE (V4L2_CID_PRIVATE_BASE+7) +#define V4L2_CID_PANTILT_RESET (V4L2_CID_PRIVATE_BASE+8) + +struct vdIn { + int fd; + char *videodevice; + char *status; + char *pictName; + struct v4l2_capability cap; + struct v4l2_format fmt; + struct v4l2_buffer buf; + struct v4l2_requestbuffers rb; + void *mem[NB_BUFFER]; + unsigned char *tmpbuffer; + unsigned char *framebuffer; + int isstreaming; + int grabmethod; + int width; + int height; + int formatIn; + int formatOut; + int framesizeIn; + int signalquit; + int toggleAvi; + int getPict; + +}; + +int + init_videoIn (struct vdIn *vd, char *device, int width, int height, + int format, int grabmethod); +int uvcGrab (struct vdIn *vd); +int close_v4l2 (struct vdIn *vd); + +int v4l2GetControl (struct vdIn *vd, int control); +int v4l2SetControl (struct vdIn *vd, int control, int value); +int v4l2UpControl (struct vdIn *vd, int control); +int v4l2DownControl (struct vdIn *vd, int control); +int v4l2ToggleControl (struct vdIn *vd, int control); +int v4l2ResetControl (struct vdIn *vd, int control); +int v4l2ResetPanTilt (struct vdIn *vd, int pantilt); +int v4L2UpDownPan (struct vdIn *vd, short inc); +int v4L2UpDownTilt (struct vdIn *vd, short inc); diff --git a/HenocUniverse/Makefile b/HenocUniverse/Makefile new file mode 100755 index 0000000..d75669e --- /dev/null +++ b/HenocUniverse/Makefile @@ -0,0 +1,46 @@ +OPTIONS=-O2 +INCLUDES="/usr/include/malloc/" + +all: + @echo "compiling henocUniverse..." + @g++ -c source/shapes.cpp -I. -I$(INCLUDES) -o .obj/shapes.o $(OPTIONS) + @g++ -c source/vector.cpp -I. -I$(INCLUDES) -o .obj/vector.o $(OPTIONS) + @g++ -c source/henocUniverse.cpp -I. -I$(INCLUDES) -o .obj/henocUniverse.o $(OPTIONS) + @g++ -c source/intersection.cpp -I. -I$(INCLUDES) -o .obj/intersection.o $(OPTIONS) + @g++ -c source/circle-circle.cpp -I. -I$(INCLUDES) -o .obj/circle-circle.o $(OPTIONS) + @g++ -c source/quad-quad.cpp -I. -I$(INCLUDES) -o .obj/quad-quad.o $(OPTIONS) + @g++ -c source/quad-circle.cpp -I. -I$(INCLUDES) -o .obj/quad-circle.o $(OPTIONS) + @g++ -c source/terrain-all.cpp -I. -I$(INCLUDES) -o .obj/terrain-all.o $(OPTIONS) + @g++ -c source/composite-all.cpp -I. -I$(INCLUDES) -o .obj/composite-all.o $(OPTIONS) + @echo "compiling ODE..." + @g++ -c ode/source/array.cpp -I. -I$(INCLUDES) -o .obj/array.o $(OPTIONS) + @g++ -c ode/source/joint.cpp -I. -I$(INCLUDES) -o .obj/joint.o $(OPTIONS) + @g++ -c ode/source/mass.cpp -I. -I$(INCLUDES) -o .obj/mass.o $(OPTIONS) + @g++ -c ode/source/matrix.cpp -I. -I$(INCLUDES) -o .obj/matrix.o $(OPTIONS) + @g++ -c ode/source/misc.cpp -I. -I$(INCLUDES) -o .obj/misc.o $(OPTIONS) + @g++ -c ode/source/ode.cpp -I. -I$(INCLUDES) -o .obj/ode.o $(OPTIONS) + @g++ -c ode/source/quickstep.cpp -I. -I$(INCLUDES) -o .obj/quickstep.o $(OPTIONS) + @g++ -c ode/source/step.cpp -I. -I$(INCLUDES) -o .obj/step.o $(OPTIONS) + @g++ -c ode/source/testing.cpp -I. -I$(INCLUDES) -o .obj/testing.o $(OPTIONS) + @g++ -c ode/source/util.cpp -I. -I$(INCLUDES) -o .obj/util.o $(OPTIONS) + @g++ -c ode/source/error.cpp -I. -I$(INCLUDES) -o .obj/error.o $(OPTIONS) + @g++ -c ode/source/lcp.cpp -I. -I$(INCLUDES) -o .obj/lcp.o $(OPTIONS) + @g++ -c ode/source/mat.cpp -I. -I$(INCLUDES) -o .obj/mat.o $(OPTIONS) + @g++ -c ode/source/memory.cpp -I. -I$(INCLUDES) -o .obj/memory.o $(OPTIONS) + @g++ -c ode/source/obstack.cpp -I. -I$(INCLUDES) -o .obj/obstack.o $(OPTIONS) + @g++ -c ode/source/odemath.cpp -I. -I$(INCLUDES) -o .obj/odemath.o $(OPTIONS) + @g++ -c ode/source/rotation.cpp -I. -I$(INCLUDES) -o .obj/rotation.o $(OPTIONS) + @g++ -c ode/source/stepfast.cpp -I. -I$(INCLUDES) -o .obj/stepfast.o $(OPTIONS) + @g++ -c ode/source/timer.cpp -I. -I$(INCLUDES) -o .obj/timer.o $(OPTIONS) + @g++ -c ode/source/fastdot.c -I. -I$(INCLUDES) -o .obj/fastdot.o $(OPTIONS) + @g++ -c ode/source/fastldlt.c -I. -I$(INCLUDES) -o .obj/fastldlt.o $(OPTIONS) + @g++ -c ode/source/fastlsolve.c -I. -I$(INCLUDES) -o .obj/fastlsolve.o $(OPTIONS) + @g++ -c ode/source/fastltsolve.c -I. -I$(INCLUDES) -o .obj/fastltsolve.o $(OPTIONS) + @echo "linking libHenocUniverse..." + @ar rcs libHenocUniverse.a .obj/shapes.o .obj/vector.o .obj/henocUniverse.o .obj/intersection.o .obj/circle-circle.o .obj/quad-quad.o .obj/quad-circle.o .obj/terrain-all.o .obj/composite-all.o .obj/array.o .obj/joint.o .obj/mass.o .obj/matrix.o .obj/misc.o .obj/ode.o .obj/quickstep.o .obj/step.o .obj/testing.o .obj/util.o .obj/error.o .obj/lcp.o .obj/mat.o .obj/memory.o .obj/obstack.o .obj/odemath.o .obj/rotation.o .obj/stepfast.o .obj/timer.o .obj/fastdot.o .obj/fastldlt.o .obj/fastlsolve.o .obj/fastltsolve.o + +clean: + @echo "cleaning henocUniverse..." + @rm -fv .obj/* + @rm -fv libHenocUniverse.a + diff --git a/HenocUniverse/aabb.h b/HenocUniverse/aabb.h new file mode 100755 index 0000000..c7371d2 --- /dev/null +++ b/HenocUniverse/aabb.h @@ -0,0 +1,24 @@ +/* + * TT102 HENOC + * 2008 @ ESCOM-IPN + * + */ +#ifndef _HENOC_AABB_H +#define _HENOC_AABB_H + +namespace HenocUniverse{ + struct aabb{ + aabb() {} + aabb(float left, float top, float right, float bottom) : + left(left), top(top), right(right), bottom(bottom) {} + bool intersects(const aabb &x) const{ + return ((left < x.right && x.left < right) && (top < x.bottom && x.top < bottom)); + } + float left; + float top; + float right; + float bottom; + }; +} + +#endif diff --git a/HenocUniverse/enums.h b/HenocUniverse/enums.h new file mode 100755 index 0000000..26a727d --- /dev/null +++ b/HenocUniverse/enums.h @@ -0,0 +1,47 @@ +/* + * TT102 HENOC + * 2008 @ ESCOM-IPN + * + */ + +#ifndef _HENOC_ENUMS_H +#define _HENOC_ENUMS_H + +namespace HenocUniverse{ + static const char *ShapeTable[] ={ + "Quad", + "Line", + "Circle", + "Terrain", + "Composite", + }; + + struct Shape{ + enum EShape{ + Quad = 0, + Line = 1, + Circle = 2, + Terrain = 3, + Composite = 4, + Count = 5, + Invalid = 5, + }; + + union{ + EShape name; + unsigned int value; + }; + + Shape(EShape name) : name(name) {} + Shape(unsigned int value) : value(value) {} + Shape() : value(Invalid) {} + operator EShape() const { return name; } + unsigned int operator++() { return ++value; } + unsigned int operator++(int) { return value++; } + unsigned int operator--() { return --value; } + unsigned int operator--(int) { return value--; } + const char *GetString() const { return ShapeTable[value]; } + }; +} + +#endif diff --git a/HenocUniverse/henocUniverse.h b/HenocUniverse/henocUniverse.h new file mode 100755 index 0000000..2e01140 --- /dev/null +++ b/HenocUniverse/henocUniverse.h @@ -0,0 +1,249 @@ +/* + * TT102 HENOC + * 2008 @ ESCOM-IPN + * + */ +#ifndef _HENOC_HENOCUNIVERSE_H +#define _HENOC_HENOCUNIVERSE_H + + +#include +#include +#include +#include +#include +#include +#include +#include + + +// Colisiones en 2D +namespace HenocUniverse{ + class ContactList; + class Object; + + typedef unsigned int Mask; + typedef dBodyID Body; + typedef void (*Callback)(ContactList &contacts); + + // Clase abstracta para la deteccion de colisiones. + class Geometry{ + public: + Geometry(); + virtual void UpdateBounds() = 0; + virtual void SetCenter(const vec2 ¢er); + vec2 Center() const { return center; } + virtual void SetAxis(const vec2 &axis); + void Rotate(float theta); + void Rotate(const vec2 &xform); + vec2 Axis() const { return axis; } + vec2 Axis(int i) const { return i ? axis.perp() : axis; } + virtual void SetMass(Body body, float density) const {} + virtual Shape GetShape() const = 0; + const aabb &GetBounds() const { return bounds; } + protected: + aabb bounds; + vec2 axis; + vec2 center; + }; + + + // Propiedades Fisicas. + struct ObjectProperties{ + float density; + float friction; + float bounceFactor; + float bounceVelocity; + Mask collisionMask; + Mask frictionMask; + Callback callback; + }; + + //Clase abstracta base para todos los objetos, ya sean dinamicos o estaticos + class Object{ + public: + Object(); + virtual ~Object() {} + virtual Geometry &GetGeometry() = 0; + virtual const Geometry &GetGeometry() const = 0; + virtual bool IsDynamic() const = 0; + virtual Body GetBody() = 0; + virtual void SetMass(float density) = 0; + void Move(); + void Rotate(float theta); + void SetCenter(const vec2 ¢er); + public: + ObjectProperties &Property() { return properties; } + const ObjectProperties &Property() const { return properties; } + static ObjectProperties &Default() { return defaults; } + static void PushProperties() { defaultStack.push(defaults); } + static void PopProperties() { defaults = defaultStack.top(); defaultStack.pop(); } + private: + ObjectProperties properties; + static ObjectProperties defaults; + static std::stack defaultStack; + }; + + //Objetos Dinamicos. + template + class Dynamic : public Object{ + public: + Dynamic(G *geometry, Body b); + virtual ~Dynamic() { dBodyDestroy(body); delete geometry; } + G &GetGeometry() { return *geometry; } + const G &GetGeometry() const { return *geometry; } + bool IsDynamic() const { return true; } + Body GetBody() { return body; } + void SetMass(float density) { geometry->SetMass(body, density); } + protected: + G *geometry; + Body body; + }; + + //Objetos estaticos. Los que estan anclados al sistema. + template + class Static : public Object{ + public: + Static(G *geometry) : geometry(geometry) {} + virtual ~Static() { delete geometry; } + G &GetGeometry() { return *geometry; } + const G &GetGeometry() const { return *geometry; } + bool IsDynamic() const { return false; } + Body GetBody() { return 0; } + void SetMass(float) {} + protected: + G *geometry; + }; + + // Un array entre 2 figuras geometricas en contacto. + class ContactList{ + public: + ContactList(); + void Reset(Object *o1, Object *o2); + Object *Self() { return o1; } + Object *Other() { return o2; } + void ToggleNormalInversion() { invertNormals = !invertNormals; } + void AddContact(const vec2 &position, const vec2 &normal, float depth); + void Finalize(); + void CreateJoints(dWorldID world, dJointGroupID contactGroup) const; + int Count() const { return count; } + static const int Max = 64; + private: + Object *o1; + Object *o2; + dContact contacts[Max]; + int count; + bool invertNormals; + }; + + + class World{ + public: + World(); + ~World(); + void QuickStep(float); + Body BodyCreate(); + template void GenerateContacts(const S &space); + template bool IsCorrupt(const S &space) const; + int ContactCount() const { return contactCount; } + void SetCFM(float); + void SetAutoDisableFlag(bool); + void SetERP(float); + void SetContactMaxCorrectingVel(float); + void SetContactSurfaceLayer(float); + void SetAutoDisableLinearThreshold(float); + void SetAutoDisableAngularThreshold(float); + void SetGravity(const vec2&); + dJointID AddMotor(Object &object); + dJointID Glue(Object &object1, Object &object2); + dJointID AnchorAxis(Object &object, const vec2 &axis); + dJointID Anchor(Object &o1, Object &o2, const vec2 &point, float mu, float erp); + dJointID Anchor(Object &o1, const vec2 &point, float mu, float erp); + void DeleteJoint(dJointID joint); + static void SetMotorVelocity(dJointID joint, float velocity); + static float GetMotorVelocity(dJointID joint); + private: + int contactCount; + dWorldID world; + dJointGroupID contactGroup; + ContactList contactList; + }; + + // Generador de contactos 2. + template + void World::GenerateContacts(const Container &space){ + dJointGroupEmpty(contactGroup); + contactCount = 0; + typename Container::const_iterator o1; + for (o1 = space.begin(); o1 != space.end(); ++o1){ + Object *object1 = (*o1)->GetFlatlandObject(); + if (!object1) + continue; + typename Container::const_iterator o2 = o1; + for (++o2; o2 != space.end(); ++o2){ + Object *object2 = (*o2)->GetFlatlandObject(); + if (!object2) + continue; + if (!object1->IsDynamic() && !object2->IsDynamic()) + continue; + if (!(object1->Property().collisionMask & object2->Property().collisionMask)) + if (!object1->Property().callback && !object2->Property().callback) + continue; + + Geometry &g1 = object1->GetGeometry(); + Geometry &g2 = object2->GetGeometry(); + + if (Intersection::Test(g1, g2)){ + contactList.Reset(object1, object2); + Intersection::Find(g1, g2, contactList); + contactList.Finalize(); + if ((object1->Property().collisionMask & object2->Property().collisionMask)){ + contactList.CreateJoints(world, contactGroup); + contactCount += contactList.Count(); + } + } + } + } + } + + + // Deteccion de errores. + template + bool World::IsCorrupt(const Container &space) const{ + typename Container::const_iterator o; + for (o = space.begin(); o != space.end(); ++o){ + Object *object = (*o)->GetFlatlandObject(); + if (!object) continue; + Body body = object->GetBody(); + if (!body) continue; + + const dReal *lvel = dBodyGetLinearVel(body); + const dReal *avel = dBodyGetAngularVel(body); + + if (is_nan(lvel[0])) return true; + if (is_nan(lvel[1])) return true; + if (is_nan(avel[0])) return true; + if (is_nan(avel[1])) return true; + } + return false; + } + + + template + Dynamic::Dynamic(G *g, Body b) : geometry(g), body(b){ + body->geom = this; + dMatrix3 R; + float theta = atan2f(geometry->Axis(0).y, geometry->Axis(0).x); // TODO inefficient + dRFromAxisAndAngle(R, 0, 0, 1, theta); + dBodyInit(body, geometry->Center().x, geometry->Center().y, R); + SetMass(Object::Default().density); + } +} + +typedef HenocUniverse::Object *dGeomID; +void dGeomMoved(dGeomID g); +dGeomID dGeomGetBodyNext(dGeomID); +void dGeomSetBody(dGeomID g, dBodyID b); + + +#endif diff --git a/HenocUniverse/intersection.h b/HenocUniverse/intersection.h new file mode 100755 index 0000000..c01df7b --- /dev/null +++ b/HenocUniverse/intersection.h @@ -0,0 +1,64 @@ +/* + * TT102 HENOC + * 2008 @ ESCOM-IPN + * + */ +#ifndef _HENOC_INTERSECTION_H +#define _HENOC_INTERSECTION_H + + +namespace HenocUniverse{ + class Geometry; + class ContactList; + class Quad; + class Circle; + class Terrain; + + namespace Intersection{ + void Find(const Geometry &g1, const Geometry &g2, ContactList &contacts); + + void FindQuadQuad(const Geometry &g1, const Geometry &g2, ContactList &contacts); + void FindQuadCircle(const Geometry &g1, const Geometry &g2, ContactList &contacts); + void FindQuadTerrain(const Geometry &g1, const Geometry &g2, ContactList &contacts); + void FindQuadComposite(const Geometry &g1, const Geometry &g2, ContactList &contacts); + + void FindCircleQuad(const Geometry &g1, const Geometry &g2, ContactList &contacts); + void FindCircleCircle(const Geometry &g1, const Geometry &g2, ContactList &contacts); + void FindCircleTerrain(const Geometry &g1, const Geometry &g2, ContactList &contacts); + void FindCircleComposite(const Geometry &g1, const Geometry &g2, ContactList &contacts); + + void FindTerrainQuad(const Geometry &g1, const Geometry &g2, ContactList &contacts); + void FindTerrainCircle(const Geometry &g1, const Geometry &g2, ContactList &contacts); + void FindTerrainTerrain(const Geometry &g1, const Geometry &g2, ContactList &contacts); + void FindTerrainComposite(const Geometry &g1, const Geometry &g2, ContactList &contacts); + + void FindCompositeQuad(const Geometry &g1, const Geometry &g2, ContactList &contacts); + void FindCompositeCircle(const Geometry &g1, const Geometry &g2, ContactList &contacts); + void FindCompositeTerrain(const Geometry &g1, const Geometry &g2, ContactList &contacts); + void FindCompositeComposite(const Geometry &g1, const Geometry &g2, ContactList &contacts); + + bool Test(const Geometry &g1, const Geometry &g2); + + bool TestQuadQuad(const Geometry &g1, const Geometry &g2); + bool TestQuadCircle(const Geometry &g1, const Geometry &g2); + bool TestQuadTerrain(const Geometry &g1, const Geometry &g2); + bool TestQuadComposite(const Geometry &g1, const Geometry &g2); + + bool TestCircleQuad(const Geometry &g1, const Geometry &g2); + bool TestCircleCircle(const Geometry &g1, const Geometry &g2); + bool TestCircleTerrain(const Geometry &g1, const Geometry &g2); + bool TestCircleComposite(const Geometry &g1, const Geometry &g2); + + bool TestTerrainQuad(const Geometry &g1, const Geometry &g2); + bool TestTerrainCircle(const Geometry &g1, const Geometry &g2); + bool TestTerrainTerrain(const Geometry &g1, const Geometry &g2); + bool TestTerrainComposite(const Geometry &g1, const Geometry &g2); + + bool TestCompositeQuad(const Geometry &g1, const Geometry &g2); + bool TestCompositeCircle(const Geometry &g1, const Geometry &g2); + bool TestCompositeTerrain(const Geometry &g1, const Geometry &g2); + bool TestCompositeComposite(const Geometry &g1, const Geometry &g2); + } +} + +#endif diff --git a/HenocUniverse/ode/LICENSE-BSD.TXT b/HenocUniverse/ode/LICENSE-BSD.TXT new file mode 100755 index 0000000..485e164 --- /dev/null +++ b/HenocUniverse/ode/LICENSE-BSD.TXT @@ -0,0 +1,34 @@ + +This is the BSD-style license for the Open Dynamics Engine +---------------------------------------------------------- + +Open Dynamics Engine +Copyright (c) 2001-2004, Russell L. Smith. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +Neither the names of ODE's copyright owner nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/HenocUniverse/ode/LICENSE.TXT b/HenocUniverse/ode/LICENSE.TXT new file mode 100755 index 0000000..cfe59bc --- /dev/null +++ b/HenocUniverse/ode/LICENSE.TXT @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library 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.1 of the License, or (at your option) any later version. + + This library 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; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/HenocUniverse/ode/common.h b/HenocUniverse/ode/common.h new file mode 100755 index 0000000..334377d --- /dev/null +++ b/HenocUniverse/ode/common.h @@ -0,0 +1,322 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef _ODE_COMMON_H_ +#define _ODE_COMMON_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* configuration stuff */ + +/* the efficient alignment. most platforms align data structures to some + * number of bytes, but this is not always the most efficient alignment. + * for example, many x86 compilers align to 4 bytes, but on a pentium it + * is important to align doubles to 8 byte boundaries (for speed), and + * the 4 floats in a SIMD register to 16 byte boundaries. many other + * platforms have similar behavior. setting a larger alignment can waste + * a (very) small amount of memory. NOTE: this number must be a power of + * two. this is set to 16 by default. + */ +#define EFFICIENT_ALIGNMENT 16 + + +/* constants */ + +/* pi and 1/sqrt(2) are defined here if necessary because they don't get + * defined in on some platforms (like MS-Windows) + */ + +#ifndef M_PI +#define M_PI REAL(3.1415926535897932384626433832795029) +#endif +#ifndef M_SQRT1_2 +#define M_SQRT1_2 REAL(0.7071067811865475244008443621048490) +#endif + + +/* debugging: + * IASSERT is an internal assertion, i.e. a consistency check. if it fails + * we want to know where. + * UASSERT is a user assertion, i.e. if it fails a nice error message + * should be printed for the user. + * AASSERT is an arguments assertion, i.e. if it fails "bad argument(s)" + * is printed. + * DEBUGMSG just prints out a message + */ + +#ifndef dNODEBUG +#ifdef __GNUC__ +#define dIASSERT(a) if (!(a)) dDebug (d_ERR_IASSERT, \ + "assertion \"" #a "\" failed in %s() [%s]",__FUNCTION__,__FILE__); +#define dUASSERT(a,msg) if (!(a)) dDebug (d_ERR_UASSERT, \ + msg " in %s()", __FUNCTION__); +#define dDEBUGMSG(msg) dMessage (d_ERR_UASSERT, \ + msg " in %s()", __FUNCTION__); +#else +#define dIASSERT(a) if (!(a)) dDebug (d_ERR_IASSERT, \ + "assertion \"" #a "\" failed in %s:%d",__FILE__,__LINE__); +#define dUASSERT(a,msg) if (!(a)) dDebug (d_ERR_UASSERT, \ + msg " (%s:%d)", __FILE__,__LINE__); +#define dDEBUGMSG(msg) dMessage (d_ERR_UASSERT, \ + msg " (%s:%d)", __FILE__,__LINE__); +#endif +#else +#define dIASSERT(a) ; +#define dUASSERT(a,msg) ; +#define dDEBUGMSG(msg) ; +#endif +#define dAASSERT(a) dUASSERT(a,"Bad argument(s)") + +/* floating point data type, vector, matrix and quaternion types */ + +#if defined(dSINGLE) +typedef float dReal; +#elif defined(dDOUBLE) +typedef double dReal; +#else +#error You must #define dSINGLE or dDOUBLE +#endif + + +/* round an integer up to a multiple of 4, except that 0 and 1 are unmodified + * (used to compute matrix leading dimensions) + */ +#define dPAD(a) (((a) > 1) ? ((((a)-1)|3)+1) : (a)) + +/* these types are mainly just used in headers */ +typedef dReal dVector3[4]; +typedef dReal dVector4[4]; +typedef dReal dMatrix3[4*3]; +typedef dReal dMatrix4[4*4]; +typedef dReal dMatrix6[8*6]; +typedef dReal dQuaternion[4]; + + +/* precision dependent scalar math functions */ + +#if defined(dSINGLE) + +#define REAL(x) (x ## f) /* form a constant */ +#define dRecip(x) ((float)(1.0/(x))) /* reciprocal */ +#define dSqrt(x) ((float)sqrtf(float(x))) /* square root */ +#define dRecipSqrt(x) ((float)(1.0/sqrtf(float(x)))) /* reciprocal square root */ +#define dSin(x) ((float)sinf(float(x))) /* sine */ +#define dCos(x) ((float)cosf(float(x))) /* cosine */ +#define dFabs(x) ((float)fabsf(float(x))) /* absolute value */ +#define dAtan2(y,x) ((float)atan2(float(y),float(x))) /* arc tangent with 2 args */ +#define dFMod(a,b) ((float)fmodf(float(a),float(b))) /* modulo */ +#define dCopySign(a,b) ((float)copysignf(float(a),float(b))) + +#elif defined(dDOUBLE) + +#define REAL(x) (x) +#define dRecip(x) (1.0/(x)) +#define dSqrt(x) sqrt(x) +#define dRecipSqrt(x) (1.0/sqrt(x)) +#define dSin(x) sin(x) +#define dCos(x) cos(x) +#define dFabs(x) fabs(x) +#define dAtan2(y,x) atan2((y),(x)) +#define dFMod(a,b) (fmod((a),(b))) +#define dCopySign(a,b) (copysign((a),(b))) + +#else +#error You must #define dSINGLE or dDOUBLE +#endif + + +/* utility */ + + +/* round something up to be a multiple of the EFFICIENT_ALIGNMENT */ + +#define dEFFICIENT_SIZE(x) ((((x)-1)|(EFFICIENT_ALIGNMENT-1))+1) + + +/* alloca aligned to the EFFICIENT_ALIGNMENT. note that this can waste + * up to 15 bytes per allocation, depending on what alloca() returns. + */ + +#define dALLOCA16(n) \ + ((char*)dEFFICIENT_SIZE(((size_t)(alloca((n)+(EFFICIENT_ALIGNMENT-1)))))) + + +/* internal object types (all prefixed with `dx') */ + +struct dxWorld; /* dynamics world */ +//struct dxSpace; // removed for flatland +struct dxBody; /* rigid body (dynamics object) */ +//struct dxGeom; // removed for flatland +struct dxJoint; +struct dxJointNode; +struct dxJointGroup; + +typedef struct dxWorld *dWorldID; +//typedef struct dxSpace *dSpaceID; // removed for flatland +typedef struct dxBody *dBodyID; +//typedef struct dxGeom *dGeomID; // removed for flatland +typedef struct dxJoint *dJointID; +typedef struct dxJointGroup *dJointGroupID; + + +/* error numbers */ + +enum { + d_ERR_UNKNOWN = 0, /* unknown error */ + d_ERR_IASSERT, /* internal assertion failed */ + d_ERR_UASSERT, /* user assertion failed */ + d_ERR_LCP /* user assertion failed */ +}; + + +/* joint type numbers */ + +enum { + dJointTypeNone = 0, /* or "unknown" */ + dJointTypeBall, + dJointTypeHinge, + dJointTypeSlider, + dJointTypeContact, + dJointTypeUniversal, + dJointTypeHinge2, + dJointTypeFixed, + dJointTypeNull, + dJointTypeAMotor +}; + + +/* an alternative way of setting joint parameters, using joint parameter + * structures and member constants. we don't actually do this yet. + */ + +/* +typedef struct dLimot { + int mode; + dReal lostop, histop; + dReal vel, fmax; + dReal fudge_factor; + dReal bounce, soft; + dReal suspension_erp, suspension_cfm; +} dLimot; + +enum { + dLimotLoStop = 0x0001, + dLimotHiStop = 0x0002, + dLimotVel = 0x0004, + dLimotFMax = 0x0008, + dLimotFudgeFactor = 0x0010, + dLimotBounce = 0x0020, + dLimotSoft = 0x0040 +}; +*/ + + +/* standard joint parameter names. why are these here? - because we don't want + * to include all the joint function definitions in joint.cpp. hmmmm. + * MSVC complains if we call D_ALL_PARAM_NAMES_X with a blank second argument, + * which is why we have the D_ALL_PARAM_NAMES macro as well. please copy and + * paste between these two. + */ + +#define D_ALL_PARAM_NAMES(start) \ + /* parameters for limits and motors */ \ + dParamLoStop = start, \ + dParamHiStop, \ + dParamVel, \ + dParamFMax, \ + dParamFudgeFactor, \ + dParamBounce, \ + dParamCFM, \ + dParamStopERP, \ + dParamStopCFM, \ + /* parameters for suspension */ \ + dParamSuspensionERP, \ + dParamSuspensionCFM, + +#define D_ALL_PARAM_NAMES_X(start,x) \ + /* parameters for limits and motors */ \ + dParamLoStop ## x = start, \ + dParamHiStop ## x, \ + dParamVel ## x, \ + dParamFMax ## x, \ + dParamFudgeFactor ## x, \ + dParamBounce ## x, \ + dParamCFM ## x, \ + dParamStopERP ## x, \ + dParamStopCFM ## x, \ + /* parameters for suspension */ \ + dParamSuspensionERP ## x, \ + dParamSuspensionCFM ## x, + +enum { + D_ALL_PARAM_NAMES(0) + D_ALL_PARAM_NAMES_X(0x100,2) + D_ALL_PARAM_NAMES_X(0x200,3) + + /* add a multiple of this constant to the basic parameter numbers to get + * the parameters for the second, third etc axes. + */ + dParamGroup=0x100 +}; + + +/* angular motor mode numbers */ + +enum{ + dAMotorUser = 0, + dAMotorEuler = 1 +}; + + +/* joint force feedback information */ + +typedef struct dJointFeedback { + dVector3 f1; /* force applied to body 1 */ + dVector3 t1; /* torque applied to body 1 */ + dVector3 f2; /* force applied to body 2 */ + dVector3 t2; /* torque applied to body 2 */ +} dJointFeedback; + + +/* private functions that must be implemented by the collision library: + * (1) indicate that a geom has moved, (2) get the next geom in a body list. + * these functions are called whenever the position of geoms connected to a + * body have changed, e.g. with dBodySetPosition(), dBodySetRotation(), or + * when the ODE step function updates the body state. + */ + +#ifdef __cplusplus +} +namespace HenocUniverse { class Object; } // added for flatland +typedef class HenocUniverse::Object* dGeomID; // added for flatland +typedef class HenocUniverse::Object dxGeom; // added for flatland +void dGeomMoved (dGeomID); // moved for flatland +dGeomID dGeomGetBodyNext (dGeomID); // moved for flatland +#endif + +#endif diff --git a/HenocUniverse/ode/compatibility.h b/HenocUniverse/ode/compatibility.h new file mode 100755 index 0000000..b370986 --- /dev/null +++ b/HenocUniverse/ode/compatibility.h @@ -0,0 +1,40 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef _ODE_COMPATIBILITY_H_ +#define _ODE_COMPATIBILITY_H_ + +/* + * ODE's backward compatibility system ensures that as ODE's API + * evolves, user code will not break. + */ + +/* + * These new rotation function names are more consistent with the + * rest of the API. + */ +#define dQtoR(q,R) dRfromQ((R),(q)) +#define dRtoQ(R,q) dQfromR((q),(R)) +#define dWtoDQ(w,q,dq) dDQfromW((dq),(w),(q)) + + +#endif diff --git a/HenocUniverse/ode/config.h b/HenocUniverse/ode/config.h new file mode 100755 index 0000000..d1095e0 --- /dev/null +++ b/HenocUniverse/ode/config.h @@ -0,0 +1,125 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* per-machine configuration */ + +#ifndef _ODE_CONFIG_H_ +#define _ODE_CONFIG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include // for alloca under windows +#include +#include + +//#define dDOUBLE +#define dSINGLE + +#if defined(WIN32) && (defined(_MSC_VER) || defined(MINGW)) +static union { unsigned char __c[4]; float __f; } __ode_huge_valf = + {{0,0,0x80,0x7f}}; +#define _INFINITY4 (__ode_huge_valf.__f) +static union { unsigned char __c[8]; double __d; } __ode_huge_val = + {{0,0,0,0,0,0,0xf0,0x7f }}; +#define _INFINITY8 (__ode_huge_val.__d) +#else +#define _INFINITY8 HUGE_VAL +#define _INFINITY4 HUGE_VALF +#endif + +#if defined(dSINGLE) +#define dInfinity _INFINITY4 +#else +#define dInfinity _INFINITY8 +#endif + +/* some types. assume `int' >= 32 bits */ +typedef unsigned int uint; +typedef int int32; +typedef unsigned int uint32; +typedef short int16; +typedef unsigned short uint16; +typedef char int8; +typedef unsigned char uint8; + + +/* an integer type that we can safely cast a pointer to and from without + * loss of bits. + */ +#ifdef __x86_64 +typedef unsigned long long intP; // should I use -m32 instead? +#else +typedef uint32 intP; +#endif + + +/* if we're compiling on a pentium, we may need to know the clock rate so + * that the timing function can report accurate times. this number only needs + * to be set accurately if we're doing performance tests - otherwise just + * ignore this. i have not worked out how to determine this number + * automatically yet. + */ + +#ifdef PENTIUM +#ifndef PENTIUM_HZ +#define PENTIUM_HZ (496.318983e6) +#endif +#endif + + +/* the efficient alignment. most platforms align data structures to some + * number of bytes, but this is not always the most efficient alignment. + * for example, many x86 compilers align to 4 bytes, but on a pentium it is + * important to align doubles to 8 byte boundaries (for speed), and the 4 + * floats in a SIMD register to 16 byte boundaries. many other platforms have + * similar behavior. setting a larger alignment can waste a (very) small + * amount of memory. NOTE: this number must be a power of two. + */ + +#define EFFICIENT_ALIGNMENT 16 + + +/* for unix, define this if your system supports anonymous memory maps + * (linux does). + */ + +#define MMAP_ANONYMOUS + +#ifdef WIN32 +#define snprintf _snprintf +#define vsnprintf _vsnprintf +#define copysignf _copysign +#define copysign _copysign +#endif + +#include + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/HenocUniverse/ode/contact.h b/HenocUniverse/ode/contact.h new file mode 100755 index 0000000..926d77f --- /dev/null +++ b/HenocUniverse/ode/contact.h @@ -0,0 +1,90 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef _ODE_CONTACT_H_ +#define _ODE_CONTACT_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +enum { + dContactMu2 = 0x001, + dContactFDir1 = 0x002, + dContactBounce = 0x004, + dContactSoftERP = 0x008, + dContactSoftCFM = 0x010, + dContactMotion1 = 0x020, + dContactMotion2 = 0x040, + dContactSlip1 = 0x080, + dContactSlip2 = 0x100, + + dContactApprox0 = 0x0000, + dContactApprox1_1 = 0x1000, + dContactApprox1_2 = 0x2000, + dContactApprox1 = 0x3000 +}; + + +typedef struct dSurfaceParameters { + /* must always be defined */ + int mode; + dReal mu; + + /* only defined if the corresponding flag is set in mode */ + dReal mu2; + dReal bounce; + dReal bounce_vel; + dReal soft_erp; + dReal soft_cfm; + dReal motion1,motion2; + dReal slip1,slip2; +} dSurfaceParameters; + + +/* contact info set by collision functions */ + +typedef struct dContactGeom { + dVector3 pos; + dVector3 normal; + dReal depth; + dGeomID g1,g2; +} dContactGeom; + + +/* contact info used by contact joint */ + +typedef struct dContact { + dSurfaceParameters surface; + dContactGeom geom; + dVector3 fdir1; +} dContact; + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/HenocUniverse/ode/error.h b/HenocUniverse/ode/error.h new file mode 100755 index 0000000..700bf44 --- /dev/null +++ b/HenocUniverse/ode/error.h @@ -0,0 +1,63 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* this comes from the `reuse' library. copy any changes back to the source */ + +#ifndef _ODE_ERROR_H_ +#define _ODE_ERROR_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* all user defined error functions have this type. error and debug functions + * should not return. + */ +typedef void dMessageFunction (int errnum, const char *msg, va_list ap); + +/* set a new error, debug or warning handler. if fn is 0, the default handlers + * are used. + */ +void dSetErrorHandler (dMessageFunction *fn); +void dSetDebugHandler (dMessageFunction *fn); +void dSetMessageHandler (dMessageFunction *fn); + +/* return the current error, debug or warning handler. if the return value is + * 0, the default handlers are in place. + */ +dMessageFunction *dGetErrorHandler(); +dMessageFunction *dGetDebugHandler(); +dMessageFunction *dGetMessageHandler(); + +/* generate a fatal error, debug trap or a message. */ +void dError (int num, const char *msg, ...); +void dDebug (int num, const char *msg, ...); +void dMessage (int num, const char *msg, ...); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/HenocUniverse/ode/mass.h b/HenocUniverse/ode/mass.h new file mode 100755 index 0000000..f94fa56 --- /dev/null +++ b/HenocUniverse/ode/mass.h @@ -0,0 +1,107 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef _ODE_MASS_H_ +#define _ODE_MASS_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct dMass; +typedef struct dMass dMass; + + +void dMassSetZero (dMass *); + +void dMassSetParameters (dMass *, dReal themass, + dReal cgx, dReal cgy, dReal cgz, + dReal I11, dReal I22, dReal I33, + dReal I12, dReal I13, dReal I23); + +void dMassSetSphere (dMass *, dReal density, dReal radius); +void dMassSetSphereTotal (dMass *, dReal total_mass, dReal radius); + +void dMassSetCappedCylinder (dMass *, dReal density, int direction, + dReal radius, dReal length); +void dMassSetCappedCylinderTotal (dMass *, dReal total_mass, int direction, + dReal radius, dReal length); + +void dMassSetCylinder (dMass *, dReal density, int direction, + dReal radius, dReal length); +void dMassSetCylinderTotal (dMass *, dReal total_mass, int direction, + dReal radius, dReal length); + +void dMassSetBox (dMass *, dReal density, + dReal lx, dReal ly, dReal lz); +void dMassSetBoxTotal (dMass *, dReal total_mass, + dReal lx, dReal ly, dReal lz); + +void dMassAdjust (dMass *, dReal newmass); + +void dMassTranslate (dMass *, dReal x, dReal y, dReal z); + +void dMassRotate (dMass *, const dMatrix3 R); + +void dMassAdd (dMass *a, const dMass *b); + + + +struct dMass { + dReal mass; + dVector4 c; + dMatrix3 I; + +#ifdef __cplusplus + dMass() + { dMassSetZero (this); } + void setZero() + { dMassSetZero (this); } + void setParameters (dReal themass, dReal cgx, dReal cgy, dReal cgz, + dReal I11, dReal I22, dReal I33, + dReal I12, dReal I13, dReal I23) + { dMassSetParameters (this,themass,cgx,cgy,cgz,I11,I22,I33,I12,I13,I23); } + void setSphere (dReal density, dReal radius) + { dMassSetSphere (this,density,radius); } + void setCappedCylinder (dReal density, int direction, dReal a, dReal b) + { dMassSetCappedCylinder (this,density,direction,a,b); } + void setBox (dReal density, dReal lx, dReal ly, dReal lz) + { dMassSetBox (this,density,lx,ly,lz); } + void adjust (dReal newmass) + { dMassAdjust (this,newmass); } + void translate (dReal x, dReal y, dReal z) + { dMassTranslate (this,x,y,z); } + void rotate (const dMatrix3 R) + { dMassRotate (this,R); } + void add (const dMass *b) + { dMassAdd (this,b); } +#endif +}; + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/HenocUniverse/ode/matrix.h b/HenocUniverse/ode/matrix.h new file mode 100755 index 0000000..75218fd --- /dev/null +++ b/HenocUniverse/ode/matrix.h @@ -0,0 +1,194 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* optimized and unoptimized vector and matrix functions */ + +#ifndef _ODE_MATRIX_H_ +#define _ODE_MATRIX_H_ + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* set a vector/matrix of size n to all zeros, or to a specific value. */ + +void dSetZero (dReal *a, int n); +void dSetValue (dReal *a, int n, dReal value); + + +/* get the dot product of two n*1 vectors. if n <= 0 then + * zero will be returned (in which case a and b need not be valid). + */ + +dReal dDot (const dReal *a, const dReal *b, int n); + + +/* get the dot products of (a0,b), (a1,b), etc and return them in outsum. + * all vectors are n*1. if n <= 0 then zeroes will be returned (in which case + * the input vectors need not be valid). this function is somewhat faster + * than calling dDot() for all of the combinations separately. + */ + +/* NOT INCLUDED in the library for now. +void dMultidot2 (const dReal *a0, const dReal *a1, + const dReal *b, dReal *outsum, int n); +*/ + + +/* matrix multiplication. all matrices are stored in standard row format. + * the digit refers to the argument that is transposed: + * 0: A = B * C (sizes: A:p*r B:p*q C:q*r) + * 1: A = B' * C (sizes: A:p*r B:q*p C:q*r) + * 2: A = B * C' (sizes: A:p*r B:p*q C:r*q) + * case 1,2 are equivalent to saying that the operation is A=B*C but + * B or C are stored in standard column format. + */ + +void dMultiply0 (dReal *A, const dReal *B, const dReal *C, int p,int q,int r); +void dMultiply1 (dReal *A, const dReal *B, const dReal *C, int p,int q,int r); +void dMultiply2 (dReal *A, const dReal *B, const dReal *C, int p,int q,int r); + + +/* do an in-place cholesky decomposition on the lower triangle of the n*n + * symmetric matrix A (which is stored by rows). the resulting lower triangle + * will be such that L*L'=A. return 1 on success and 0 on failure (on failure + * the matrix is not positive definite). + */ + +int dFactorCholesky (dReal *A, int n); + + +/* solve for x: L*L'*x = b, and put the result back into x. + * L is size n*n, b is size n*1. only the lower triangle of L is considered. + */ + +void dSolveCholesky (const dReal *L, dReal *b, int n); + + +/* compute the inverse of the n*n positive definite matrix A and put it in + * Ainv. this is not especially fast. this returns 1 on success (A was + * positive definite) or 0 on failure (not PD). + */ + +int dInvertPDMatrix (const dReal *A, dReal *Ainv, int n); + + +/* check whether an n*n matrix A is positive definite, return 1/0 (yes/no). + * positive definite means that x'*A*x > 0 for any x. this performs a + * cholesky decomposition of A. if the decomposition fails then the matrix + * is not positive definite. A is stored by rows. A is not altered. + */ + +int dIsPositiveDefinite (const dReal *A, int n); + + +/* factorize a matrix A into L*D*L', where L is lower triangular with ones on + * the diagonal, and D is diagonal. + * A is an n*n matrix stored by rows, with a leading dimension of n rounded + * up to 4. L is written into the strict lower triangle of A (the ones are not + * written) and the reciprocal of the diagonal elements of D are written into + * d. + */ +void dFactorLDLT (dReal *A, dReal *d, int n, int nskip); + + +/* solve L*x=b, where L is n*n lower triangular with ones on the diagonal, + * and x,b are n*1. b is overwritten with x. + * the leading dimension of L is `nskip'. + */ +void dSolveL1 (const dReal *L, dReal *b, int n, int nskip); + + +/* solve L'*x=b, where L is n*n lower triangular with ones on the diagonal, + * and x,b are n*1. b is overwritten with x. + * the leading dimension of L is `nskip'. + */ +void dSolveL1T (const dReal *L, dReal *b, int n, int nskip); + + +/* in matlab syntax: a(1:n) = a(1:n) .* d(1:n) */ + +void dVectorScale (dReal *a, const dReal *d, int n); + + +/* given `L', a n*n lower triangular matrix with ones on the diagonal, + * and `d', a n*1 vector of the reciprocal diagonal elements of an n*n matrix + * D, solve L*D*L'*x=b where x,b are n*1. x overwrites b. + * the leading dimension of L is `nskip'. + */ + +void dSolveLDLT (const dReal *L, const dReal *d, dReal *b, int n, int nskip); + + +/* given an L*D*L' factorization of an n*n matrix A, return the updated + * factorization L2*D2*L2' of A plus the following "top left" matrix: + * + * [ b a' ] <-- b is a[0] + * [ a 0 ] <-- a is a[1..n-1] + * + * - L has size n*n, its leading dimension is nskip. L is lower triangular + * with ones on the diagonal. only the lower triangle of L is referenced. + * - d has size n. d contains the reciprocal diagonal elements of D. + * - a has size n. + * the result is written into L, except that the left column of L and d[0] + * are not actually modified. see ldltaddTL.m for further comments. + */ +void dLDLTAddTL (dReal *L, dReal *d, const dReal *a, int n, int nskip); + + +/* given an L*D*L' factorization of a permuted matrix A, produce a new + * factorization for row and column `r' removed. + * - A has size n1*n1, its leading dimension in nskip. A is symmetric and + * positive definite. only the lower triangle of A is referenced. + * A itself may actually be an array of row pointers. + * - L has size n2*n2, its leading dimension in nskip. L is lower triangular + * with ones on the diagonal. only the lower triangle of L is referenced. + * - d has size n2. d contains the reciprocal diagonal elements of D. + * - p is a permutation vector. it contains n2 indexes into A. each index + * must be in the range 0..n1-1. + * - r is the row/column of L to remove. + * the new L will be written within the old L, i.e. will have the same leading + * dimension. the last row and column of L, and the last element of d, are + * undefined on exit. + * + * a fast O(n^2) algorithm is used. see ldltremove.m for further comments. + */ +void dLDLTRemove (dReal **A, const int *p, dReal *L, dReal *d, + int n1, int n2, int r, int nskip); + + +/* given an n*n matrix A (with leading dimension nskip), remove the r'th row + * and column by moving elements. the new matrix will have the same leading + * dimension. the last row and column of A are untouched on exit. + */ +void dRemoveRowCol (dReal *A, int n, int nskip, int r); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/HenocUniverse/ode/memory.h b/HenocUniverse/ode/memory.h new file mode 100755 index 0000000..ff57f5f --- /dev/null +++ b/HenocUniverse/ode/memory.h @@ -0,0 +1,59 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* this comes from the `reuse' library. copy any changes back to the source */ + +#ifndef _ODE_MEMORY_H_ +#define _ODE_MEMORY_H_ + +#include "ode/config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* function types to allocate and free memory */ +typedef void * dAllocFunction (size_t size); +typedef void * dReallocFunction (void *ptr, size_t oldsize, size_t newsize); +typedef void dFreeFunction (void *ptr, size_t size); + +/* set new memory management functions. if fn is 0, the default handlers are + * used. */ +void dSetAllocHandler (dAllocFunction *fn); +void dSetReallocHandler (dReallocFunction *fn); +void dSetFreeHandler (dFreeFunction *fn); + +/* get current memory management functions */ +dAllocFunction *dGetAllocHandler (); +dReallocFunction *dGetReallocHandler (); +dFreeFunction *dGetFreeHandler (); + +/* allocate and free memory. */ +void * dAlloc (size_t size); +void * dRealloc (void *ptr, size_t oldsize, size_t newsize); +void dFree (void *ptr, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/HenocUniverse/ode/misc.h b/HenocUniverse/ode/misc.h new file mode 100755 index 0000000..bc0270c --- /dev/null +++ b/HenocUniverse/ode/misc.h @@ -0,0 +1,85 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* miscellaneous math functions. these are mostly useful for testing */ + +#ifndef _ODE_MISC_H_ +#define _ODE_MISC_H_ + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* return 1 if the random number generator is working. */ +int dTestRand(); + +/* return next 32 bit random number. this uses a not-very-random linear + * congruential method. + */ +unsigned long dRand(); + +/* get and set the current random number seed. */ +unsigned long dRandGetSeed(); +void dRandSetSeed (unsigned long s); + +/* return a random integer between 0..n-1. the distribution will get worse + * as n approaches 2^32. + */ +int dRandInt (int n); + +/* return a random real number between 0..1 */ +dReal dRandReal(); + +/* print out a matrix */ +#ifdef __cplusplus +void dPrintMatrix (const dReal *A, int n, int m, char *fmt = "%10.4f ", + FILE *f=stdout); +#else +void dPrintMatrix (const dReal *A, int n, int m, char *fmt, FILE *f); +#endif + +/* make a random vector with entries between +/- range. A has n elements. */ +void dMakeRandomVector (dReal *A, int n, dReal range); + +/* make a random matrix with entries between +/- range. A has size n*m. */ +void dMakeRandomMatrix (dReal *A, int n, int m, dReal range); + +/* clear the upper triangle of a square matrix */ +void dClearUpperTriangle (dReal *A, int n); + +/* return the maximum element difference between the two n*m matrices */ +dReal dMaxDifference (const dReal *A, const dReal *B, int n, int m); + +/* return the maximum element difference between the lower triangle of two + * n*n matrices */ +dReal dMaxDifferenceLowerTriangle (const dReal *A, const dReal *B, int n); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/HenocUniverse/ode/objects.h b/HenocUniverse/ode/objects.h new file mode 100755 index 0000000..fa8fe2c --- /dev/null +++ b/HenocUniverse/ode/objects.h @@ -0,0 +1,266 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef _ODE_OBJECTS_H_ +#define _ODE_OBJECTS_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* world */ + +dWorldID dWorldCreate(); +void dWorldDestroy (dWorldID); + +void dWorldSetGravity (dWorldID, dReal x, dReal y, dReal z); +void dWorldGetGravity (dWorldID, dVector3 gravity); +void dWorldSetERP (dWorldID, dReal erp); +dReal dWorldGetERP (dWorldID); +void dWorldSetCFM (dWorldID, dReal cfm); +dReal dWorldGetCFM (dWorldID); +void dWorldStep (dWorldID, dReal stepsize); +void dWorldImpulseToForce (dWorldID, dReal stepsize, + dReal ix, dReal iy, dReal iz, dVector3 force); + +/* World QuickStep functions */ + +void dWorldQuickStep (dWorldID w, dReal stepsize); +void dWorldSetQuickStepNumIterations (dWorldID, int num); +int dWorldGetQuickStepNumIterations (dWorldID); +void dWorldSetQuickStepW (dWorldID, dReal param); +dReal dWorldGetQuickStepW (dWorldID); + +/* World contact parameter functions */ + +void dWorldSetContactMaxCorrectingVel (dWorldID, dReal vel); +dReal dWorldGetContactMaxCorrectingVel (dWorldID); +void dWorldSetContactSurfaceLayer (dWorldID, dReal depth); +dReal dWorldGetContactSurfaceLayer (dWorldID); + +/* StepFast1 functions */ + +void dWorldStepFast1(dWorldID, dReal stepsize, int maxiterations); +void dWorldSetAutoEnableDepthSF1(dWorldID, int autoEnableDepth); +int dWorldGetAutoEnableDepthSF1(dWorldID); + +/* Auto-disable functions */ + +dReal dWorldGetAutoDisableLinearThreshold (dWorldID); +void dWorldSetAutoDisableLinearThreshold (dWorldID, dReal linear_threshold); +dReal dWorldGetAutoDisableAngularThreshold (dWorldID); +void dWorldSetAutoDisableAngularThreshold (dWorldID, dReal angular_threshold); +int dWorldGetAutoDisableSteps (dWorldID); +void dWorldSetAutoDisableSteps (dWorldID, int steps); +dReal dWorldGetAutoDisableTime (dWorldID); +void dWorldSetAutoDisableTime (dWorldID, dReal time); +int dWorldGetAutoDisableFlag (dWorldID); +void dWorldSetAutoDisableFlag (dWorldID, int do_auto_disable); + +dReal dBodyGetAutoDisableLinearThreshold (dBodyID); +void dBodySetAutoDisableLinearThreshold (dBodyID, dReal linear_threshold); +dReal dBodyGetAutoDisableAngularThreshold (dBodyID); +void dBodySetAutoDisableAngularThreshold (dBodyID, dReal angular_threshold); +int dBodyGetAutoDisableSteps (dBodyID); +void dBodySetAutoDisableSteps (dBodyID, int steps); +dReal dBodyGetAutoDisableTime (dBodyID); +void dBodySetAutoDisableTime (dBodyID, dReal time); +int dBodyGetAutoDisableFlag (dBodyID); +void dBodySetAutoDisableFlag (dBodyID, int do_auto_disable); +void dBodySetAutoDisableDefaults (dBodyID); + +/* bodies */ + +dBodyID dBodyCreate (dWorldID); +void dBodyDestroy (dBodyID); + +void dBodySetData (dBodyID, void *data); +void *dBodyGetData (dBodyID); + +void dBodyInit(dBodyID b, dReal x, dReal y, const dMatrix3 R); +void dBodySetPosition (dBodyID, dReal x, dReal y, dReal z); +void dBodySetRotation (dBodyID, const dMatrix3 R); +void dBodySetQuaternion (dBodyID, const dQuaternion q); +void dBodySetLinearVel (dBodyID, dReal x, dReal y, dReal z); +void dBodySetAngularVel (dBodyID, dReal x, dReal y, dReal z); +const dReal * dBodyGetPosition (dBodyID); +const dReal * dBodyGetRotation (dBodyID); /* ptr to 4x3 rot matrix */ +const dReal * dBodyGetQuaternion (dBodyID); +const dReal * dBodyGetLinearVel (dBodyID); +const dReal * dBodyGetAngularVel (dBodyID); + +void dBodySetMass (dBodyID, const dMass *mass); +void dBodyGetMass (dBodyID, dMass *mass); + +void dBodyAddForce (dBodyID, dReal fx, dReal fy, dReal fz); +void dBodyAddTorque (dBodyID, dReal fx, dReal fy, dReal fz); +void dBodyAddRelForce (dBodyID, dReal fx, dReal fy, dReal fz); +void dBodyAddRelTorque (dBodyID, dReal fx, dReal fy, dReal fz); +void dBodyAddForceAtPos (dBodyID, dReal fx, dReal fy, dReal fz, + dReal px, dReal py, dReal pz); +void dBodyAddForceAtRelPos (dBodyID, dReal fx, dReal fy, dReal fz, + dReal px, dReal py, dReal pz); +void dBodyAddRelForceAtPos (dBodyID, dReal fx, dReal fy, dReal fz, + dReal px, dReal py, dReal pz); +void dBodyAddRelForceAtRelPos (dBodyID, dReal fx, dReal fy, dReal fz, + dReal px, dReal py, dReal pz); + +const dReal * dBodyGetForce (dBodyID); +const dReal * dBodyGetTorque (dBodyID); +void dBodySetForce (dBodyID b, dReal x, dReal y, dReal z); +void dBodySetTorque (dBodyID b, dReal x, dReal y, dReal z); + +void dBodyGetRelPointPos (dBodyID, dReal px, dReal py, dReal pz, + dVector3 result); +void dBodyGetRelPointVel (dBodyID, dReal px, dReal py, dReal pz, + dVector3 result); +void dBodyGetPointVel (dBodyID, dReal px, dReal py, dReal pz, + dVector3 result); +void dBodyGetPosRelPoint (dBodyID, dReal px, dReal py, dReal pz, + dVector3 result); +void dBodyVectorToWorld (dBodyID, dReal px, dReal py, dReal pz, + dVector3 result); +void dBodyVectorFromWorld (dBodyID, dReal px, dReal py, dReal pz, + dVector3 result); + +void dBodySetFiniteRotationMode (dBodyID, int mode); +void dBodySetFiniteRotationAxis (dBodyID, dReal x, dReal y, dReal z); + +int dBodyGetFiniteRotationMode (dBodyID); +void dBodyGetFiniteRotationAxis (dBodyID, dVector3 result); + +int dBodyGetNumJoints (dBodyID b); +dJointID dBodyGetJoint (dBodyID, int index); + +void dBodyEnable (dBodyID); +void dBodyDisable (dBodyID); +int dBodyIsEnabled (dBodyID); + +void dBodySetGravityMode (dBodyID b, int mode); +int dBodyGetGravityMode (dBodyID b); + + +/* joints */ + +dJointID dJointCreateBall (dWorldID, dJointGroupID); +dJointID dJointCreateHinge (dWorldID, dJointGroupID); +dJointID dJointCreateSlider (dWorldID, dJointGroupID); +dJointID dJointCreateContact (dWorldID, dJointGroupID, const dContact *); +dJointID dJointCreateHinge2 (dWorldID, dJointGroupID); +dJointID dJointCreateUniversal (dWorldID, dJointGroupID); +dJointID dJointCreateFixed (dWorldID, dJointGroupID); +dJointID dJointCreateNull (dWorldID, dJointGroupID); +dJointID dJointCreateAMotor (dWorldID, dJointGroupID); + +void dJointDestroy (dJointID); + +dJointGroupID dJointGroupCreate (int max_size); +void dJointGroupDestroy (dJointGroupID); +void dJointGroupEmpty (dJointGroupID); + +void dJointAttach (dJointID, dBodyID body1, dBodyID body2); +void dJointSetData (dJointID, void *data); +void *dJointGetData (dJointID); +int dJointGetType (dJointID); +dBodyID dJointGetBody (dJointID, int index); + +void dJointSetFeedback (dJointID, dJointFeedback *); +dJointFeedback *dJointGetFeedback (dJointID); + +void dJointSetBallAnchor (dJointID, dReal x, dReal y, dReal z); +void dJointSetHingeAnchor (dJointID, dReal x, dReal y, dReal z); +void dJointSetHingeAxis (dJointID, dReal x, dReal y, dReal z); +void dJointSetHingeParam (dJointID, int parameter, dReal value); +void dJointAddHingeTorque(dJointID joint, dReal torque); +void dJointSetSliderAxis (dJointID, dReal x, dReal y, dReal z); +void dJointSetSliderParam (dJointID, int parameter, dReal value); +void dJointAddSliderForce(dJointID joint, dReal force); +void dJointSetHinge2Anchor (dJointID, dReal x, dReal y, dReal z); +void dJointSetHinge2Axis1 (dJointID, dReal x, dReal y, dReal z); +void dJointSetHinge2Axis2 (dJointID, dReal x, dReal y, dReal z); +void dJointSetHinge2Param (dJointID, int parameter, dReal value); +void dJointAddHinge2Torques(dJointID joint, dReal torque1, dReal torque2); +void dJointSetUniversalAnchor (dJointID, dReal x, dReal y, dReal z); +void dJointSetUniversalAxis1 (dJointID, dReal x, dReal y, dReal z); +void dJointSetUniversalAxis2 (dJointID, dReal x, dReal y, dReal z); +void dJointSetUniversalParam (dJointID, int parameter, dReal value); +void dJointAddUniversalTorques(dJointID joint, dReal torque1, dReal torque2); +void dJointSetFixed (dJointID); +void dJointSetAMotorNumAxes (dJointID, int num); +void dJointSetAMotorAxis (dJointID, int anum, int rel, + dReal x, dReal y, dReal z); +void dJointSetAMotorAngle (dJointID, int anum, dReal angle); +void dJointSetAMotorParam (dJointID, int parameter, dReal value); +void dJointSetAMotorMode (dJointID, int mode); +void dJointSetErp (dJointID, dReal); // Philip +void dJointAddAMotorTorques (dJointID, dReal torque1, dReal torque2, dReal torque3); + +void dJointGetBallAnchor (dJointID, dVector3 result); +void dJointGetBallAnchor2 (dJointID, dVector3 result); +void dJointGetHingeAnchor (dJointID, dVector3 result); +void dJointGetHingeAnchor2 (dJointID, dVector3 result); +void dJointGetHingeAxis (dJointID, dVector3 result); +dReal dJointGetHingeParam (dJointID, int parameter); +dReal dJointGetHingeAngle (dJointID); +dReal dJointGetHingeAngleRate (dJointID); +dReal dJointGetSliderPosition (dJointID); +dReal dJointGetSliderPositionRate (dJointID); +void dJointGetSliderAxis (dJointID, dVector3 result); +dReal dJointGetSliderParam (dJointID, int parameter); +void dJointGetHinge2Anchor (dJointID, dVector3 result); +void dJointGetHinge2Anchor2 (dJointID, dVector3 result); +void dJointGetHinge2Axis1 (dJointID, dVector3 result); +void dJointGetHinge2Axis2 (dJointID, dVector3 result); +dReal dJointGetHinge2Param (dJointID, int parameter); +dReal dJointGetHinge2Angle1 (dJointID); +dReal dJointGetHinge2Angle1Rate (dJointID); +dReal dJointGetHinge2Angle2Rate (dJointID); +void dJointGetUniversalAnchor (dJointID, dVector3 result); +void dJointGetUniversalAnchor2 (dJointID, dVector3 result); +void dJointGetUniversalAxis1 (dJointID, dVector3 result); +void dJointGetUniversalAxis2 (dJointID, dVector3 result); +dReal dJointGetUniversalParam (dJointID, int parameter); +dReal dJointGetUniversalAngle1 (dJointID); +dReal dJointGetUniversalAngle2 (dJointID); +dReal dJointGetUniversalAngle1Rate (dJointID); +dReal dJointGetUniversalAngle2Rate (dJointID); +int dJointGetAMotorNumAxes (dJointID); +void dJointGetAMotorAxis (dJointID, int anum, dVector3 result); +int dJointGetAMotorAxisRel (dJointID, int anum); +dReal dJointGetAMotorAngle (dJointID, int anum); +dReal dJointGetAMotorAngleRate (dJointID, int anum); +dReal dJointGetAMotorParam (dJointID, int parameter); +int dJointGetAMotorMode (dJointID); + +int dAreConnected (dBodyID, dBodyID); +int dAreConnectedExcluding (dBodyID, dBodyID, int joint_type); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/HenocUniverse/ode/ode.h b/HenocUniverse/ode/ode.h new file mode 100755 index 0000000..015f359 --- /dev/null +++ b/HenocUniverse/ode/ode.h @@ -0,0 +1,44 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef _ODE_ODE_H_ +#define _ODE_ODE_H_ + +/* include *everything* here */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif diff --git a/HenocUniverse/ode/odecpp.h b/HenocUniverse/ode/odecpp.h new file mode 100755 index 0000000..01a06e4 --- /dev/null +++ b/HenocUniverse/ode/odecpp.h @@ -0,0 +1,621 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* C++ interface for non-collision stuff */ + + +#ifndef _ODE_ODECPP_H_ +#define _ODE_ODECPP_H_ +#ifdef __cplusplus + +#include + + +class dWorld { + dWorldID _id; + + // intentionally undefined, don't use these + dWorld (const dWorld &); + void operator= (const dWorld &); + +public: + dWorld() + { _id = dWorldCreate(); } + ~dWorld() + { dWorldDestroy (_id); } + + dWorldID id() const + { return _id; } + operator dWorldID() const + { return _id; } + + void setGravity (dReal x, dReal y, dReal z) + { dWorldSetGravity (_id,x,y,z); } + void getGravity (dVector3 g) const + { dWorldGetGravity (_id,g); } + + void setERP (dReal erp) + { dWorldSetERP(_id, erp); } + dReal getERP() const + { return dWorldGetERP(_id); } + + void setCFM (dReal cfm) + { dWorldSetCFM(_id, cfm); } + dReal getCFM() const + { return dWorldGetCFM(_id); } + + void step (dReal stepsize) + { dWorldStep (_id,stepsize); } + + void stepFast1 (dReal stepsize, int maxiterations) + { dWorldStepFast1 (_id,stepsize,maxiterations); } + void setAutoEnableDepthSF1(dWorldID, int depth) + { dWorldSetAutoEnableDepthSF1 (_id, depth); } + int getAutoEnableDepthSF1(dWorldID) + { return dWorldGetAutoEnableDepthSF1 (_id); } + + void setAutoDisableLinearThreshold (dReal threshold) + { dWorldSetAutoDisableLinearThreshold (_id,threshold); } + dReal getAutoDisableLinearThreshold() + { return dWorldGetAutoDisableLinearThreshold (_id); } + void setAutoDisableAngularThreshold (dReal threshold) + { dWorldSetAutoDisableAngularThreshold (_id,threshold); } + dReal getAutoDisableAngularThreshold() + { return dWorldGetAutoDisableAngularThreshold (_id); } + void setAutoDisableSteps (int steps) + { dWorldSetAutoDisableSteps (_id,steps); } + int getAutoDisableSteps() + { return dWorldGetAutoDisableSteps (_id); } + void setAutoDisableTime (dReal time) + { dWorldSetAutoDisableTime (_id,time); } + dReal getAutoDisableTime() + { return dWorldGetAutoDisableTime (_id); } + void setAutoDisableFlag (int do_auto_disable) + { dWorldSetAutoDisableFlag (_id,do_auto_disable); } + int getAutoDisableFlag() + { return dWorldGetAutoDisableFlag (_id); } + + void impulseToForce (dReal stepsize, dReal ix, dReal iy, dReal iz, + dVector3 force) + { dWorldImpulseToForce (_id,stepsize,ix,iy,iz,force); } +}; + + +class dBody { + dBodyID _id; + + // intentionally undefined, don't use these + dBody (const dBody &); + void operator= (const dBody &); + +public: + dBody() + { _id = 0; } + dBody (dWorldID world) + { _id = dBodyCreate (world); } + ~dBody() + { if (_id) dBodyDestroy (_id); } + + void create (dWorldID world) { + if (_id) dBodyDestroy (_id); + _id = dBodyCreate (world); + } + + dBodyID id() const + { return _id; } + operator dBodyID() const + { return _id; } + + void setData (void *data) + { dBodySetData (_id,data); } + void *getData() const + { return dBodyGetData (_id); } + + void setPosition (dReal x, dReal y, dReal z) + { dBodySetPosition (_id,x,y,z); } + void setRotation (const dMatrix3 R) + { dBodySetRotation (_id,R); } + void setQuaternion (const dQuaternion q) + { dBodySetQuaternion (_id,q); } + void setLinearVel (dReal x, dReal y, dReal z) + { dBodySetLinearVel (_id,x,y,z); } + void setAngularVel (dReal x, dReal y, dReal z) + { dBodySetAngularVel (_id,x,y,z); } + + const dReal * getPosition() const + { return dBodyGetPosition (_id); } + const dReal * getRotation() const + { return dBodyGetRotation (_id); } + const dReal * getQuaternion() const + { return dBodyGetQuaternion (_id); } + const dReal * getLinearVel() const + { return dBodyGetLinearVel (_id); } + const dReal * getAngularVel() const + { return dBodyGetAngularVel (_id); } + + void setMass (const dMass *mass) + { dBodySetMass (_id,mass); } + void getMass (dMass *mass) const + { dBodyGetMass (_id,mass); } + + void addForce (dReal fx, dReal fy, dReal fz) + { dBodyAddForce (_id, fx, fy, fz); } + void addTorque (dReal fx, dReal fy, dReal fz) + { dBodyAddTorque (_id, fx, fy, fz); } + void addRelForce (dReal fx, dReal fy, dReal fz) + { dBodyAddRelForce (_id, fx, fy, fz); } + void addRelTorque (dReal fx, dReal fy, dReal fz) + { dBodyAddRelTorque (_id, fx, fy, fz); } + void addForceAtPos (dReal fx, dReal fy, dReal fz, + dReal px, dReal py, dReal pz) + { dBodyAddForceAtPos (_id, fx, fy, fz, px, py, pz); } + void addForceAtRelPos (dReal fx, dReal fy, dReal fz, + dReal px, dReal py, dReal pz) + { dBodyAddForceAtRelPos (_id, fx, fy, fz, px, py, pz); } + void addRelForceAtPos (dReal fx, dReal fy, dReal fz, + dReal px, dReal py, dReal pz) + { dBodyAddRelForceAtPos (_id, fx, fy, fz, px, py, pz); } + void addRelForceAtRelPos (dReal fx, dReal fy, dReal fz, + dReal px, dReal py, dReal pz) + { dBodyAddRelForceAtRelPos (_id, fx, fy, fz, px, py, pz); } + + const dReal * getForce() const + { return dBodyGetForce(_id); } + const dReal * getTorque() const + { return dBodyGetTorque(_id); } + void setForce (dReal x, dReal y, dReal z) + { dBodySetForce (_id,x,y,z); } + void setTorque (dReal x, dReal y, dReal z) + { dBodySetTorque (_id,x,y,z); } + + void enable() + { dBodyEnable (_id); } + void disable() + { dBodyDisable (_id); } + int isEnabled() const + { return dBodyIsEnabled (_id); } + + void getRelPointPos (dReal px, dReal py, dReal pz, dVector3 result) const + { dBodyGetRelPointPos (_id, px, py, pz, result); } + void getRelPointVel (dReal px, dReal py, dReal pz, dVector3 result) const + { dBodyGetRelPointVel (_id, px, py, pz, result); } + void getPointVel (dReal px, dReal py, dReal pz, dVector3 result) const + { dBodyGetPointVel (_id,px,py,pz,result); } + void getPosRelPoint (dReal px, dReal py, dReal pz, dVector3 result) const + { dBodyGetPosRelPoint (_id,px,py,pz,result); } + void vectorToWorld (dReal px, dReal py, dReal pz, dVector3 result) const + { dBodyVectorToWorld (_id,px,py,pz,result); } + void vectorFromWorld (dReal px, dReal py, dReal pz, dVector3 result) const + { dBodyVectorFromWorld (_id,px,py,pz,result); } + + void setFiniteRotationMode (int mode) + { dBodySetFiniteRotationMode (_id, mode); } + void setFiniteRotationAxis (dReal x, dReal y, dReal z) + { dBodySetFiniteRotationAxis (_id, x, y, z); } + + int getFiniteRotationMode() const + { return dBodyGetFiniteRotationMode (_id); } + void getFiniteRotationAxis (dVector3 result) const + { dBodyGetFiniteRotationAxis (_id, result); } + + int getNumJoints() const + { return dBodyGetNumJoints (_id); } + dJointID getJoint (int index) const + { return dBodyGetJoint (_id, index); } + + void setGravityMode (int mode) + { dBodySetGravityMode (_id,mode); } + int getGravityMode() const + { return dBodyGetGravityMode (_id); } + + int isConnectedTo (dBodyID body) const + { return dAreConnected (_id, body); } + + void setAutoDisableLinearThreshold (dReal threshold) + { dBodySetAutoDisableLinearThreshold (_id,threshold); } + dReal getAutoDisableLinearThreshold() + { return dBodyGetAutoDisableLinearThreshold (_id); } + void setAutoDisableAngularThreshold (dReal threshold) + { dBodySetAutoDisableAngularThreshold (_id,threshold); } + dReal getAutoDisableAngularThreshold() + { return dBodyGetAutoDisableAngularThreshold (_id); } + void setAutoDisableSteps (int steps) + { dBodySetAutoDisableSteps (_id,steps); } + int getAutoDisableSteps() + { return dBodyGetAutoDisableSteps (_id); } + void setAutoDisableTime (dReal time) + { dBodySetAutoDisableTime (_id,time); } + dReal getAutoDisableTime() + { return dBodyGetAutoDisableTime (_id); } + void setAutoDisableFlag (int do_auto_disable) + { dBodySetAutoDisableFlag (_id,do_auto_disable); } + int getAutoDisableFlag() + { return dBodyGetAutoDisableFlag (_id); } +}; + + +class dJointGroup { + dJointGroupID _id; + + // intentionally undefined, don't use these + dJointGroup (const dJointGroup &); + void operator= (const dJointGroup &); + +public: + dJointGroup (int dummy_arg=0) + { _id = dJointGroupCreate (0); } + ~dJointGroup() + { dJointGroupDestroy (_id); } + void create (int dummy_arg=0) { + if (_id) dJointGroupDestroy (_id); + _id = dJointGroupCreate (0); + } + + dJointGroupID id() const + { return _id; } + operator dJointGroupID() const + { return _id; } + + void empty() + { dJointGroupEmpty (_id); } +}; + + +class dJoint { +private: + // intentionally undefined, don't use these + dJoint (const dJoint &) ; + void operator= (const dJoint &); + +protected: + dJointID _id; + +public: + dJoint() + { _id = 0; } + ~dJoint() + { if (_id) dJointDestroy (_id); } + + dJointID id() const + { return _id; } + operator dJointID() const + { return _id; } + + void attach (dBodyID body1, dBodyID body2) + { dJointAttach (_id, body1, body2); } + + void setData (void *data) + { dJointSetData (_id, data); } + void *getData() const + { return dJointGetData (_id); } + + int getType() const + { return dJointGetType (_id); } + + dBodyID getBody (int index) const + { return dJointGetBody (_id, index); } +}; + + +class dBallJoint : public dJoint { +private: + // intentionally undefined, don't use these + dBallJoint (const dBallJoint &); + void operator= (const dBallJoint &); + +public: + dBallJoint() { } + dBallJoint (dWorldID world, dJointGroupID group=0) + { _id = dJointCreateBall (world, group); } + + void create (dWorldID world, dJointGroupID group=0) { + if (_id) dJointDestroy (_id); + _id = dJointCreateBall (world, group); + } + + void setAnchor (dReal x, dReal y, dReal z) + { dJointSetBallAnchor (_id, x, y, z); } + void getAnchor (dVector3 result) const + { dJointGetBallAnchor (_id, result); } + void getAnchor2 (dVector3 result) const + { dJointGetBallAnchor2 (_id, result); } +} ; + + +class dHingeJoint : public dJoint { + // intentionally undefined, don't use these + dHingeJoint (const dHingeJoint &); + void operator = (const dHingeJoint &); + +public: + dHingeJoint() { } + dHingeJoint (dWorldID world, dJointGroupID group=0) + { _id = dJointCreateHinge (world, group); } + + void create (dWorldID world, dJointGroupID group=0) { + if (_id) dJointDestroy (_id); + _id = dJointCreateHinge (world, group); + } + + void setAnchor (dReal x, dReal y, dReal z) + { dJointSetHingeAnchor (_id, x, y, z); } + void getAnchor (dVector3 result) const + { dJointGetHingeAnchor (_id, result); } + void getAnchor2 (dVector3 result) const + { dJointGetHingeAnchor2 (_id, result); } + + void setAxis (dReal x, dReal y, dReal z) + { dJointSetHingeAxis (_id, x, y, z); } + void getAxis (dVector3 result) const + { dJointGetHingeAxis (_id, result); } + + dReal getAngle() const + { return dJointGetHingeAngle (_id); } + dReal getAngleRate() const + { return dJointGetHingeAngleRate (_id); } + + void setParam (int parameter, dReal value) + { dJointSetHingeParam (_id, parameter, value); } + dReal getParam (int parameter) const + { return dJointGetHingeParam (_id, parameter); } + + void addTorque (dReal torque) + { dJointAddHingeTorque(_id, torque); } +}; + + +class dSliderJoint : public dJoint { + // intentionally undefined, don't use these + dSliderJoint (const dSliderJoint &); + void operator = (const dSliderJoint &); + +public: + dSliderJoint() { } + dSliderJoint (dWorldID world, dJointGroupID group=0) + { _id = dJointCreateSlider (world, group); } + + void create (dWorldID world, dJointGroupID group=0) { + if (_id) dJointDestroy (_id); + _id = dJointCreateSlider (world, group); + } + + void setAxis (dReal x, dReal y, dReal z) + { dJointSetSliderAxis (_id, x, y, z); } + void getAxis (dVector3 result) const + { dJointGetSliderAxis (_id, result); } + + dReal getPosition() const + { return dJointGetSliderPosition (_id); } + dReal getPositionRate() const + { return dJointGetSliderPositionRate (_id); } + + void setParam (int parameter, dReal value) + { dJointSetSliderParam (_id, parameter, value); } + dReal getParam (int parameter) const + { return dJointGetSliderParam (_id, parameter); } + + void addForce (dReal force) + { dJointAddSliderForce(_id, force); } +}; + + +class dUniversalJoint : public dJoint { + // intentionally undefined, don't use these + dUniversalJoint (const dUniversalJoint &); + void operator = (const dUniversalJoint &); + +public: + dUniversalJoint() { } + dUniversalJoint (dWorldID world, dJointGroupID group=0) + { _id = dJointCreateUniversal (world, group); } + + void create (dWorldID world, dJointGroupID group=0) { + if (_id) dJointDestroy (_id); + _id = dJointCreateUniversal (world, group); + } + + void setAnchor (dReal x, dReal y, dReal z) + { dJointSetUniversalAnchor (_id, x, y, z); } + void setAxis1 (dReal x, dReal y, dReal z) + { dJointSetUniversalAxis1 (_id, x, y, z); } + void setAxis2 (dReal x, dReal y, dReal z) + { dJointSetUniversalAxis2 (_id, x, y, z); } + void setParam (int parameter, dReal value) + { dJointSetUniversalParam (_id, parameter, value); } + + void getAnchor (dVector3 result) const + { dJointGetUniversalAnchor (_id, result); } + void getAnchor2 (dVector3 result) const + { dJointGetUniversalAnchor2 (_id, result); } + void getAxis1 (dVector3 result) const + { dJointGetUniversalAxis1 (_id, result); } + void getAxis2 (dVector3 result) const + { dJointGetUniversalAxis2 (_id, result); } + dReal getParam (int parameter) const + { return dJointGetUniversalParam (_id, parameter); } + dReal getAngle1() const + { return dJointGetUniversalAngle1 (_id); } + dReal getAngle1Rate() const + { return dJointGetUniversalAngle1Rate (_id); } + dReal getAngle2() const + { return dJointGetUniversalAngle2 (_id); } + dReal getAngle2Rate() const + { return dJointGetUniversalAngle2Rate (_id); } + + void addTorques (dReal torque1, dReal torque2) + { dJointAddUniversalTorques(_id, torque1, torque2); } +}; + + +class dHinge2Joint : public dJoint { + // intentionally undefined, don't use these + dHinge2Joint (const dHinge2Joint &); + void operator = (const dHinge2Joint &); + +public: + dHinge2Joint() { } + dHinge2Joint (dWorldID world, dJointGroupID group=0) + { _id = dJointCreateHinge2 (world, group); } + + void create (dWorldID world, dJointGroupID group=0) { + if (_id) dJointDestroy (_id); + _id = dJointCreateHinge2 (world, group); + } + + void setAnchor (dReal x, dReal y, dReal z) + { dJointSetHinge2Anchor (_id, x, y, z); } + void setAxis1 (dReal x, dReal y, dReal z) + { dJointSetHinge2Axis1 (_id, x, y, z); } + void setAxis2 (dReal x, dReal y, dReal z) + { dJointSetHinge2Axis2 (_id, x, y, z); } + + void getAnchor (dVector3 result) const + { dJointGetHinge2Anchor (_id, result); } + void getAnchor2 (dVector3 result) const + { dJointGetHinge2Anchor2 (_id, result); } + void getAxis1 (dVector3 result) const + { dJointGetHinge2Axis1 (_id, result); } + void getAxis2 (dVector3 result) const + { dJointGetHinge2Axis2 (_id, result); } + + dReal getAngle1() const + { return dJointGetHinge2Angle1 (_id); } + dReal getAngle1Rate() const + { return dJointGetHinge2Angle1Rate (_id); } + dReal getAngle2Rate() const + { return dJointGetHinge2Angle2Rate (_id); } + + void setParam (int parameter, dReal value) + { dJointSetHinge2Param (_id, parameter, value); } + dReal getParam (int parameter) const + { return dJointGetHinge2Param (_id, parameter); } + + void addTorques(dReal torque1, dReal torque2) + { dJointAddHinge2Torques(_id, torque1, torque2); } +}; + + +class dFixedJoint : public dJoint { + // intentionally undefined, don't use these + dFixedJoint (const dFixedJoint &); + void operator = (const dFixedJoint &); + +public: + dFixedJoint() { } + dFixedJoint (dWorldID world, dJointGroupID group=0) + { _id = dJointCreateFixed (world, group); } + + void create (dWorldID world, dJointGroupID group=0) { + if (_id) dJointDestroy (_id); + _id = dJointCreateFixed (world, group); + } + + void set() + { dJointSetFixed (_id); } +}; + + +class dContactJoint : public dJoint { + // intentionally undefined, don't use these + dContactJoint (const dContactJoint &); + void operator = (const dContactJoint &); + +public: + dContactJoint() { } + dContactJoint (dWorldID world, dJointGroupID group, dContact *contact) + { _id = dJointCreateContact (world, group, contact); } + + void create (dWorldID world, dJointGroupID group, dContact *contact) { + if (_id) dJointDestroy (_id); + _id = dJointCreateContact (world, group, contact); + } +}; + + +class dNullJoint : public dJoint { + // intentionally undefined, don't use these + dNullJoint (const dNullJoint &); + void operator = (const dNullJoint &); + +public: + dNullJoint() { } + dNullJoint (dWorldID world, dJointGroupID group=0) + { _id = dJointCreateNull (world, group); } + + void create (dWorldID world, dJointGroupID group=0) { + if (_id) dJointDestroy (_id); + _id = dJointCreateNull (world, group); + } +}; + + +class dAMotorJoint : public dJoint { + // intentionally undefined, don't use these + dAMotorJoint (const dAMotorJoint &); + void operator = (const dAMotorJoint &); + +public: + dAMotorJoint() { } + dAMotorJoint (dWorldID world, dJointGroupID group=0) + { _id = dJointCreateAMotor (world, group); } + + void create (dWorldID world, dJointGroupID group=0) { + if (_id) dJointDestroy (_id); + _id = dJointCreateAMotor (world, group); + } + + void setMode (int mode) + { dJointSetAMotorMode (_id, mode); } + int getMode() const + { return dJointGetAMotorMode (_id); } + + void setNumAxes (int num) + { dJointSetAMotorNumAxes (_id, num); } + int getNumAxes() const + { return dJointGetAMotorNumAxes (_id); } + + void setAxis (int anum, int rel, dReal x, dReal y, dReal z) + { dJointSetAMotorAxis (_id, anum, rel, x, y, z); } + void getAxis (int anum, dVector3 result) const + { dJointGetAMotorAxis (_id, anum, result); } + int getAxisRel (int anum) const + { return dJointGetAMotorAxisRel (_id, anum); } + + void setAngle (int anum, dReal angle) + { dJointSetAMotorAngle (_id, anum, angle); } + dReal getAngle (int anum) const + { return dJointGetAMotorAngle (_id, anum); } + dReal getAngleRate (int anum) + { return dJointGetAMotorAngleRate (_id,anum); } + + void setParam (int parameter, dReal value) + { dJointSetAMotorParam (_id, parameter, value); } + dReal getParam (int parameter) const + { return dJointGetAMotorParam (_id, parameter); } + + void addTorques(dReal torque1, dReal torque2, dReal torque3) + { dJointAddAMotorTorques(_id, torque1, torque2, torque3); } +}; + + +#endif +#endif diff --git a/HenocUniverse/ode/odemath.h b/HenocUniverse/ode/odemath.h new file mode 100755 index 0000000..2e2a7ee --- /dev/null +++ b/HenocUniverse/ode/odemath.h @@ -0,0 +1,240 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef _ODE_ODEMATH_H_ +#define _ODE_ODEMATH_H_ + +#include + +#ifdef __GNUC__ +#define PURE_INLINE extern inline +#else +#define PURE_INLINE inline +#endif + +/* + * macro to access elements i,j in an NxM matrix A, independent of the + * matrix storage convention. + */ +#define dACCESS33(A,i,j) ((A)[(i)*4+(j)]) + + +/* + * 3-way dot product. dDOTpq means that elements of `a' and `b' are spaced + * p and q indexes apart respectively. dDOT() means dDOT11. + * in C++ we could use function templates to get all the versions of these + * functions - but on some compilers this will result in sub-optimal code. + */ + +#define dDOTpq(a,b,p,q) ((a)[0]*(b)[0] + (a)[p]*(b)[q] + (a)[2*(p)]*(b)[2*(q)]) + +#ifdef __cplusplus + +PURE_INLINE dReal dDOT (const dReal *a, const dReal *b) { return dDOTpq(a,b,1,1); } +PURE_INLINE dReal dDOT13 (const dReal *a, const dReal *b) { return dDOTpq(a,b,1,3); } +PURE_INLINE dReal dDOT31 (const dReal *a, const dReal *b) { return dDOTpq(a,b,3,1); } +PURE_INLINE dReal dDOT33 (const dReal *a, const dReal *b) { return dDOTpq(a,b,3,3); } +PURE_INLINE dReal dDOT14 (const dReal *a, const dReal *b) { return dDOTpq(a,b,1,4); } +PURE_INLINE dReal dDOT41 (const dReal *a, const dReal *b) { return dDOTpq(a,b,4,1); } +PURE_INLINE dReal dDOT44 (const dReal *a, const dReal *b) { return dDOTpq(a,b,4,4); } + +#else + +#define dDOT(a,b) dDOTpq(a,b,1,1) +#define dDOT13(a,b) dDOTpq(a,b,1,3) +#define dDOT31(a,b) dDOTpq(a,b,3,1) +#define dDOT33(a,b) dDOTpq(a,b,3,3) +#define dDOT14(a,b) dDOTpq(a,b,1,4) +#define dDOT41(a,b) dDOTpq(a,b,4,1) +#define dDOT44(a,b) dDOTpq(a,b,4,4) + +#endif /* __cplusplus */ + + +/* + * cross product, set a = b x c. dCROSSpqr means that elements of `a', `b' + * and `c' are spaced p, q and r indexes apart respectively. + * dCROSS() means dCROSS111. `op' is normally `=', but you can set it to + * +=, -= etc to get other effects. + */ + +#define dCROSS(a,op,b,c) \ + (a)[0] op ((b)[1]*(c)[2] - (b)[2]*(c)[1]); \ + (a)[1] op ((b)[2]*(c)[0] - (b)[0]*(c)[2]); \ + (a)[2] op ((b)[0]*(c)[1] - (b)[1]*(c)[0]); +#define dCROSSpqr(a,op,b,c,p,q,r) \ + (a)[ 0] op ((b)[ q]*(c)[2*r] - (b)[2*q]*(c)[ r]); \ + (a)[ p] op ((b)[2*q]*(c)[ 0] - (b)[ 0]*(c)[2*r]); \ + (a)[2*p] op ((b)[ 0]*(c)[ r] - (b)[ q]*(c)[ 0]); +#define dCROSS114(a,op,b,c) dCROSSpqr(a,op,b,c,1,1,4) +#define dCROSS141(a,op,b,c) dCROSSpqr(a,op,b,c,1,4,1) +#define dCROSS144(a,op,b,c) dCROSSpqr(a,op,b,c,1,4,4) +#define dCROSS411(a,op,b,c) dCROSSpqr(a,op,b,c,4,1,1) +#define dCROSS414(a,op,b,c) dCROSSpqr(a,op,b,c,4,1,4) +#define dCROSS441(a,op,b,c) dCROSSpqr(a,op,b,c,4,4,1) +#define dCROSS444(a,op,b,c) dCROSSpqr(a,op,b,c,4,4,4) + + +/* + * set a 3x3 submatrix of A to a matrix such that submatrix(A)*b = a x b. + * A is stored by rows, and has `skip' elements per row. the matrix is + * assumed to be already zero, so this does not write zero elements! + * if (plus,minus) is (+,-) then a positive version will be written. + * if (plus,minus) is (-,+) then a negative version will be written. + */ + +#define dCROSSMAT(A,a,skip,plus,minus) \ + (A)[1] = minus (a)[2]; \ + (A)[2] = plus (a)[1]; \ + (A)[(skip)+0] = plus (a)[2]; \ + (A)[(skip)+2] = minus (a)[0]; \ + (A)[2*(skip)+0] = minus (a)[1]; \ + (A)[2*(skip)+1] = plus (a)[0]; + + +/* + * compute the distance between two 3-vectors + */ + +#ifdef __cplusplus +PURE_INLINE float dDISTANCE (const float a[3], const float b[3]) + { return (float) dSqrt( (a[0]-b[0])*(a[0]-b[0]) + (a[1]-b[1])*(a[1]-b[1]) + (a[2]-b[2])*(a[2]-b[2]) ); } +PURE_INLINE double dDISTANCE (const double a[3], const double b[3]) + { return dSqrt( (a[0]-b[0])*(a[0]-b[0]) + (a[1]-b[1])*(a[1]-b[1]) + (a[2]-b[2])*(a[2]-b[2]) ); } +#else +#define dDISTANCE(a,b) \ + (dSqrt( ((a)[0]-(b)[0])*((a)[0]-(b)[0]) + ((a)[1]-(b)[1])*((a)[1]-(b)[1]) + ((a)[2]-(b)[2])*((a)[2]-(b)[2]) )) +#endif + + +/* + * special case matrix multipication, with operator selection + */ + +#define dMULTIPLYOP0_331(A,op,B,C) \ + (A)[0] op dDOT((B),(C)); \ + (A)[1] op dDOT((B+4),(C)); \ + (A)[2] op dDOT((B+8),(C)); +#define dMULTIPLYOP1_331(A,op,B,C) \ + (A)[0] op dDOT41((B),(C)); \ + (A)[1] op dDOT41((B+1),(C)); \ + (A)[2] op dDOT41((B+2),(C)); +#define dMULTIPLYOP0_133(A,op,B,C) \ + (A)[0] op dDOT14((B),(C)); \ + (A)[1] op dDOT14((B),(C+1)); \ + (A)[2] op dDOT14((B),(C+2)); +#define dMULTIPLYOP0_333(A,op,B,C) \ + (A)[0] op dDOT14((B),(C)); \ + (A)[1] op dDOT14((B),(C+1)); \ + (A)[2] op dDOT14((B),(C+2)); \ + (A)[4] op dDOT14((B+4),(C)); \ + (A)[5] op dDOT14((B+4),(C+1)); \ + (A)[6] op dDOT14((B+4),(C+2)); \ + (A)[8] op dDOT14((B+8),(C)); \ + (A)[9] op dDOT14((B+8),(C+1)); \ + (A)[10] op dDOT14((B+8),(C+2)); +#define dMULTIPLYOP1_333(A,op,B,C) \ + (A)[0] op dDOT44((B),(C)); \ + (A)[1] op dDOT44((B),(C+1)); \ + (A)[2] op dDOT44((B),(C+2)); \ + (A)[4] op dDOT44((B+1),(C)); \ + (A)[5] op dDOT44((B+1),(C+1)); \ + (A)[6] op dDOT44((B+1),(C+2)); \ + (A)[8] op dDOT44((B+2),(C)); \ + (A)[9] op dDOT44((B+2),(C+1)); \ + (A)[10] op dDOT44((B+2),(C+2)); +#define dMULTIPLYOP2_333(A,op,B,C) \ + (A)[0] op dDOT((B),(C)); \ + (A)[1] op dDOT((B),(C+4)); \ + (A)[2] op dDOT((B),(C+8)); \ + (A)[4] op dDOT((B+4),(C)); \ + (A)[5] op dDOT((B+4),(C+4)); \ + (A)[6] op dDOT((B+4),(C+8)); \ + (A)[8] op dDOT((B+8),(C)); \ + (A)[9] op dDOT((B+8),(C+4)); \ + (A)[10] op dDOT((B+8),(C+8)); + +#ifdef __cplusplus + +#define DECL template PURE_INLINE void + +DECL dMULTIPLY0_331(TA *A, const TB *B, const TC *C) { dMULTIPLYOP0_331(A,=,B,C) } +DECL dMULTIPLY1_331(TA *A, const TB *B, const TC *C) { dMULTIPLYOP1_331(A,=,B,C) } +DECL dMULTIPLY0_133(TA *A, const TB *B, const TC *C) { dMULTIPLYOP0_133(A,=,B,C) } +DECL dMULTIPLY0_333(TA *A, const TB *B, const TC *C) { dMULTIPLYOP0_333(A,=,B,C) } +DECL dMULTIPLY1_333(TA *A, const TB *B, const TC *C) { dMULTIPLYOP1_333(A,=,B,C) } +DECL dMULTIPLY2_333(TA *A, const TB *B, const TC *C) { dMULTIPLYOP2_333(A,=,B,C) } + +DECL dMULTIPLYADD0_331(TA *A, const TB *B, const TC *C) { dMULTIPLYOP0_331(A,+=,B,C) } +DECL dMULTIPLYADD1_331(TA *A, const TB *B, const TC *C) { dMULTIPLYOP1_331(A,+=,B,C) } +DECL dMULTIPLYADD0_133(TA *A, const TB *B, const TC *C) { dMULTIPLYOP0_133(A,+=,B,C) } +DECL dMULTIPLYADD0_333(TA *A, const TB *B, const TC *C) { dMULTIPLYOP0_333(A,+=,B,C) } +DECL dMULTIPLYADD1_333(TA *A, const TB *B, const TC *C) { dMULTIPLYOP1_333(A,+=,B,C) } +DECL dMULTIPLYADD2_333(TA *A, const TB *B, const TC *C) { dMULTIPLYOP2_333(A,+=,B,C) } + +#undef DECL + +#else + +#define dMULTIPLY0_331(A,B,C) dMULTIPLYOP0_331(A,=,B,C) +#define dMULTIPLY1_331(A,B,C) dMULTIPLYOP1_331(A,=,B,C) +#define dMULTIPLY0_133(A,B,C) dMULTIPLYOP0_133(A,=,B,C) +#define dMULTIPLY0_333(A,B,C) dMULTIPLYOP0_333(A,=,B,C) +#define dMULTIPLY1_333(A,B,C) dMULTIPLYOP1_333(A,=,B,C) +#define dMULTIPLY2_333(A,B,C) dMULTIPLYOP2_333(A,=,B,C) + +#define dMULTIPLYADD0_331(A,B,C) dMULTIPLYOP0_331(A,+=,B,C) +#define dMULTIPLYADD1_331(A,B,C) dMULTIPLYOP1_331(A,+=,B,C) +#define dMULTIPLYADD0_133(A,B,C) dMULTIPLYOP0_133(A,+=,B,C) +#define dMULTIPLYADD0_333(A,B,C) dMULTIPLYOP0_333(A,+=,B,C) +#define dMULTIPLYADD1_333(A,B,C) dMULTIPLYOP1_333(A,+=,B,C) +#define dMULTIPLYADD2_333(A,B,C) dMULTIPLYOP2_333(A,+=,B,C) + +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * normalize 3x1 and 4x1 vectors (i.e. scale them to unit length) + */ +void dNormalize3 (dVector3 a); +void dNormalize4 (dVector4 a); + + +/* + * given a unit length "normal" vector n, generate vectors p and q vectors + * that are an orthonormal basis for the plane space perpendicular to n. + * i.e. this makes p,q such that n,p,q are all perpendicular to each other. + * q will equal n x p. if n is not unit length then p will be unit length but + * q wont be. + */ + +void dPlaneSpace (const dVector3 n, dVector3 p, dVector3 q); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/HenocUniverse/ode/rotation.h b/HenocUniverse/ode/rotation.h new file mode 100755 index 0000000..951547d --- /dev/null +++ b/HenocUniverse/ode/rotation.h @@ -0,0 +1,70 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef _ODE_ROTATION_H_ +#define _ODE_ROTATION_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +void dRSetIdentity (dMatrix3 R); + +void dRFromAxisAndAngle (dMatrix3 R, dReal ax, dReal ay, dReal az, + dReal angle); + +void dRFromEulerAngles (dMatrix3 R, dReal phi, dReal theta, dReal psi); + +void dRFrom2Axes (dMatrix3 R, dReal ax, dReal ay, dReal az, + dReal bx, dReal by, dReal bz); + +void dRFromZAxis (dMatrix3 R, dReal ax, dReal ay, dReal az); + +void dQSetIdentity (dQuaternion q); + +void dQFromAxisAndAngle (dQuaternion q, dReal ax, dReal ay, dReal az, + dReal angle); + +/* Quaternion multiplication, analogous to the matrix multiplication routines. */ +/* qa = rotate by qc, then qb */ +void dQMultiply0 (dQuaternion qa, const dQuaternion qb, const dQuaternion qc); +/* qa = rotate by qc, then by inverse of qb */ +void dQMultiply1 (dQuaternion qa, const dQuaternion qb, const dQuaternion qc); +/* qa = rotate by inverse of qc, then by qb */ +void dQMultiply2 (dQuaternion qa, const dQuaternion qb, const dQuaternion qc); +/* qa = rotate by inverse of qc, then by inverse of qb */ +void dQMultiply3 (dQuaternion qa, const dQuaternion qb, const dQuaternion qc); + +void dRfromQ (dMatrix3 R, const dQuaternion q); +void dQfromR (dQuaternion q, const dMatrix3 R); +void dDQfromW (dReal dq[4], const dVector3 w, const dQuaternion q); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/HenocUniverse/ode/source/array.cpp b/HenocUniverse/ode/source/array.cpp new file mode 100755 index 0000000..cbb1a6e --- /dev/null +++ b/HenocUniverse/ode/source/array.cpp @@ -0,0 +1,80 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#include +#include +#include +#include "array.h" + + +static inline int roundUpToPowerOfTwo (int x) +{ + int i = 1; + while (i < x) i <<= 1; + return i; +} + + +void dArrayBase::_freeAll (int sizeofT) +{ + if (_data) { + if (_data == this+1) return; // if constructLocalArray() was called + dFree (_data,_anum * sizeofT); + } +} + + +void dArrayBase::_setSize (int newsize, int sizeofT) +{ + if (newsize < 0) return; + if (newsize > _anum) { + if (_data == this+1) { + // this is a no-no, because constructLocalArray() was called + dDebug (0,"setSize() out of space in LOCAL array"); + } + int newanum = roundUpToPowerOfTwo (newsize); + if (_data) _data = dRealloc (_data, _anum*sizeofT, newanum*sizeofT); + else _data = dAlloc (newanum*sizeofT); + _anum = newanum; + } + _size = newsize; +} + + +void * dArrayBase::operator new (size_t size) +{ + return dAlloc (size); +} + + +void dArrayBase::operator delete (void *ptr, size_t size) +{ + dFree (ptr,size); +} + + +void dArrayBase::constructLocalArray (int __anum) +{ + _size = 0; + _anum = __anum; + _data = this+1; +} diff --git a/HenocUniverse/ode/source/array.h b/HenocUniverse/ode/source/array.h new file mode 100755 index 0000000..307206c --- /dev/null +++ b/HenocUniverse/ode/source/array.h @@ -0,0 +1,135 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* this comes from the `reuse' library. copy any changes back to the source. + * + * Variable sized array template. The array is always stored in a contiguous + * chunk. The array can be resized. A size increase will cause more memory + * to be allocated, and may result in relocation of the array memory. + * A size decrease has no effect on the memory allocation. + * + * Array elements with constructors or destructors are not supported! + * But if you must have such elements, here's what to know/do: + * - Bitwise copy is used when copying whole arrays. + * - When copying individual items (via push(), insert() etc) the `=' + * (equals) operator is used. Thus you should define this operator to do + * a bitwise copy. You should probably also define the copy constructor. + */ + + +#ifndef _ODE_ARRAY_H_ +#define _ODE_ARRAY_H_ + +#include + + +// this base class has no constructors or destructor, for your convenience. + +class dArrayBase { +protected: + int _size; // number of elements in `data' + int _anum; // allocated number of elements in `data' + void *_data; // array data + + void _freeAll (int sizeofT); + void _setSize (int newsize, int sizeofT); + // set the array size to `newsize', allocating more memory if necessary. + // if newsize>_anum and is a power of two then this is guaranteed to + // set _size and _anum to newsize. + +public: + // not: dArrayBase () { _size=0; _anum=0; _data=0; } + + int size() const { return _size; } + int allocatedSize() const { return _anum; } + void * operator new (size_t size); + void operator delete (void *ptr, size_t size); + + void constructor() { _size=0; _anum=0; _data=0; } + // if this structure is allocated with malloc() instead of new, you can + // call this to set it up. + + void constructLocalArray (int __anum); + // this helper function allows non-reallocating arrays to be constructed + // on the stack (or in the heap if necessary). this is something of a + // kludge and should be used with extreme care. this function acts like + // a constructor - it is called on uninitialized memory that will hold the + // Array structure and the data. __anum is the number of elements that + // are allocated. the memory MUST be allocated with size: + // sizeof(ArrayBase) + __anum*sizeof(T) + // arrays allocated this way will never try to reallocate or free the + // memory - that's your job. +}; + + +template class dArray : public dArrayBase { +public: + void equals (const dArray &x) { + setSize (x.size()); + memcpy (_data,x._data,x._size * sizeof(T)); + } + + dArray () { constructor(); } + dArray (const dArray &x) { constructor(); equals (x); } + ~dArray () { _freeAll(sizeof(T)); } + void setSize (int newsize) { _setSize (newsize,sizeof(T)); } + T *data() const { return (T*) _data; } + T & operator[] (int i) const { return ((T*)_data)[i]; } + void operator = (const dArray &x) { equals (x); } + + void push (const T item) { + if (_size < _anum) _size++; else _setSize (_size+1,sizeof(T)); + memcpy (&(((T*)_data)[_size-1]), &item, sizeof(T)); + } + + void swap (dArray &x) { + int tmp1; + void *tmp2; + tmp1=_size; _size=x._size; x._size=tmp1; + tmp1=_anum; _anum=x._anum; x._anum=tmp1; + tmp2=_data; _data=x._data; x._data=tmp2; + } + + // insert the item at the position `i'. if i<0 then add the item to the + // start, if i >= size then add the item to the end of the array. + void insert (int i, const T item) { + if (_size < _anum) _size++; else _setSize (_size+1,sizeof(T)); + if (i >= (_size-1)) i = _size-1; // add to end + else { + if (i < 0) i=0; // add to start + int n = _size-1-i; + if (n>0) memmove (((T*)_data) + i+1, ((T*)_data) + i, n*sizeof(T)); + } + ((T*)_data)[i] = item; + } + + void remove (int i) { + if (i >= 0 && i < _size) { // passing this test guarantees size>0 + int n = _size-1-i; + if (n>0) memmove (((T*)_data) + i, ((T*)_data) + i+1, n*sizeof(T)); + _size--; + } + } +}; + + +#endif diff --git a/HenocUniverse/ode/source/error.cpp b/HenocUniverse/ode/source/error.cpp new file mode 100755 index 0000000..737cfb5 --- /dev/null +++ b/HenocUniverse/ode/source/error.cpp @@ -0,0 +1,166 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#include +#include + + +static dMessageFunction *error_function = 0; +static dMessageFunction *debug_function = 0; +static dMessageFunction *message_function = 0; + + +extern "C" void dSetErrorHandler (dMessageFunction *fn) +{ + error_function = fn; +} + + +extern "C" void dSetDebugHandler (dMessageFunction *fn) +{ + debug_function = fn; +} + + +extern "C" void dSetMessageHandler (dMessageFunction *fn) +{ + message_function = fn; +} + + +extern "C" dMessageFunction *dGetErrorHandler() +{ + return error_function; +} + + +extern "C" dMessageFunction *dGetDebugHandler() +{ + return debug_function; +} + + +extern "C" dMessageFunction *dGetMessageHandler() +{ + return message_function; +} + + +static void printMessage (int num, const char *msg1, const char *msg2, + va_list ap) +{ + fflush (stderr); + fflush (stdout); + if (num) fprintf (stderr,"\n%s %d: ",msg1,num); + else fprintf (stderr,"\n%s: ",msg1); + vfprintf (stderr,msg2,ap); + fprintf (stderr,"\n"); + fflush (stderr); +} + +//**************************************************************************** +// unix + +#ifndef WIN32 +#include + +extern "C" void dError (int num, const char *msg, ...) +{ + va_list ap; + va_start (ap,msg); + if (error_function) error_function (num,msg,ap); + else printMessage (num,"ODE Error",msg,ap); + exit (1); +} + + +extern "C" void dDebug (int num, const char *msg, ...) +{ + va_list ap; + va_start (ap,msg); + if (debug_function) debug_function (num,msg,ap); + else printMessage (num,"ODE INTERNAL ERROR",msg,ap); + // *((char *)0) = 0; ... commit SEGVicide + abort(); +} + + +extern "C" void dMessage (int num, const char *msg, ...) +{ + va_list ap; + va_start (ap,msg); + if (message_function) message_function (num,msg,ap); + else printMessage (num,"ODE Message",msg,ap); +} + +#endif + +//**************************************************************************** +// windows + +#ifdef WIN32 + +#include "windows.h" + + +extern "C" void dError (int num, const char *msg, ...) +{ + va_list ap; + va_start (ap,msg); + if (error_function) error_function (num,msg,ap); + else { + char s[1000],title[100]; + snprintf (title,sizeof(title),"ODE Error %d",num); + vsnprintf (s,sizeof(s),msg,ap); + s[sizeof(s)-1] = 0; + MessageBox(0,s,title,MB_OK | MB_ICONWARNING); + } + exit (1); +} + + +extern "C" void dDebug (int num, const char *msg, ...) +{ + va_list ap; + va_start (ap,msg); + if (debug_function) debug_function (num,msg,ap); + else { + char s[1000],title[100]; + snprintf (title,sizeof(title),"ODE INTERNAL ERROR %d",num); + vsnprintf (s,sizeof(s),msg,ap); + s[sizeof(s)-1] = 0; + MessageBox(0,s,title,MB_OK | MB_ICONSTOP); + } + abort(); +} + + +extern "C" void dMessage (int num, const char *msg, ...) +{ + va_list ap; + va_start (ap,msg); + if (message_function) message_function (num,msg,ap); + else printMessage (num,"ODE Message",msg,ap); +} + + +#endif diff --git a/HenocUniverse/ode/source/fastdot.c b/HenocUniverse/ode/source/fastdot.c new file mode 100755 index 0000000..148d2dd --- /dev/null +++ b/HenocUniverse/ode/source/fastdot.c @@ -0,0 +1,30 @@ +/* generated code, do not edit. */ + +#include "ode/matrix.h" + + +dReal dDot (const dReal *a, const dReal *b, int n) +{ + dReal p0,q0,m0,p1,q1,m1,sum; + sum = 0; + n -= 2; + while (n >= 0) { + p0 = a[0]; q0 = b[0]; + m0 = p0 * q0; + p1 = a[1]; q1 = b[1]; + m1 = p1 * q1; + sum += m0; + sum += m1; + a += 2; + b += 2; + n -= 2; + } + n += 2; + while (n > 0) { + sum += (*a) * (*b); + a++; + b++; + n--; + } + return sum; +} diff --git a/HenocUniverse/ode/source/fastldlt.c b/HenocUniverse/ode/source/fastldlt.c new file mode 100755 index 0000000..df2ea6e --- /dev/null +++ b/HenocUniverse/ode/source/fastldlt.c @@ -0,0 +1,381 @@ +/* generated code, do not edit. */ + +#include "ode/matrix.h" + +/* solve L*X=B, with B containing 1 right hand sides. + * L is an n*n lower triangular matrix with ones on the diagonal. + * L is stored by rows and its leading dimension is lskip. + * B is an n*1 matrix that contains the right hand sides. + * B is stored by columns and its leading dimension is also lskip. + * B is overwritten with X. + * this processes blocks of 2*2. + * if this is in the factorizer source file, n must be a multiple of 2. + */ + +static void dSolveL1_1 (const dReal *L, dReal *B, int n, int lskip1) +{ + /* declare variables - Z matrix, p and q vectors, etc */ + dReal Z11,m11,Z21,m21,p1,q1,p2,*ex; + const dReal *ell; + int i,j; + /* compute all 2 x 1 blocks of X */ + for (i=0; i < n; i+=2) { + /* compute all 2 x 1 block of X, from rows i..i+2-1 */ + /* set the Z matrix to 0 */ + Z11=0; + Z21=0; + ell = L + i*lskip1; + ex = B; + /* the inner loop that computes outer products and adds them to Z */ + for (j=i-2; j >= 0; j -= 2) { + /* compute outer product and add it to the Z matrix */ + p1=ell[0]; + q1=ex[0]; + m11 = p1 * q1; + p2=ell[lskip1]; + m21 = p2 * q1; + Z11 += m11; + Z21 += m21; + /* compute outer product and add it to the Z matrix */ + p1=ell[1]; + q1=ex[1]; + m11 = p1 * q1; + p2=ell[1+lskip1]; + m21 = p2 * q1; + /* advance pointers */ + ell += 2; + ex += 2; + Z11 += m11; + Z21 += m21; + /* end of inner loop */ + } + /* compute left-over iterations */ + j += 2; + for (; j > 0; j--) { + /* compute outer product and add it to the Z matrix */ + p1=ell[0]; + q1=ex[0]; + m11 = p1 * q1; + p2=ell[lskip1]; + m21 = p2 * q1; + /* advance pointers */ + ell += 1; + ex += 1; + Z11 += m11; + Z21 += m21; + } + /* finish computing the X(i) block */ + Z11 = ex[0] - Z11; + ex[0] = Z11; + p1 = ell[lskip1]; + Z21 = ex[1] - Z21 - p1*Z11; + ex[1] = Z21; + /* end of outer loop */ + } +} + +/* solve L*X=B, with B containing 2 right hand sides. + * L is an n*n lower triangular matrix with ones on the diagonal. + * L is stored by rows and its leading dimension is lskip. + * B is an n*2 matrix that contains the right hand sides. + * B is stored by columns and its leading dimension is also lskip. + * B is overwritten with X. + * this processes blocks of 2*2. + * if this is in the factorizer source file, n must be a multiple of 2. + */ + +static void dSolveL1_2 (const dReal *L, dReal *B, int n, int lskip1) +{ + /* declare variables - Z matrix, p and q vectors, etc */ + dReal Z11,m11,Z12,m12,Z21,m21,Z22,m22,p1,q1,p2,q2,*ex; + const dReal *ell; + int i,j; + /* compute all 2 x 2 blocks of X */ + for (i=0; i < n; i+=2) { + /* compute all 2 x 2 block of X, from rows i..i+2-1 */ + /* set the Z matrix to 0 */ + Z11=0; + Z12=0; + Z21=0; + Z22=0; + ell = L + i*lskip1; + ex = B; + /* the inner loop that computes outer products and adds them to Z */ + for (j=i-2; j >= 0; j -= 2) { + /* compute outer product and add it to the Z matrix */ + p1=ell[0]; + q1=ex[0]; + m11 = p1 * q1; + q2=ex[lskip1]; + m12 = p1 * q2; + p2=ell[lskip1]; + m21 = p2 * q1; + m22 = p2 * q2; + Z11 += m11; + Z12 += m12; + Z21 += m21; + Z22 += m22; + /* compute outer product and add it to the Z matrix */ + p1=ell[1]; + q1=ex[1]; + m11 = p1 * q1; + q2=ex[1+lskip1]; + m12 = p1 * q2; + p2=ell[1+lskip1]; + m21 = p2 * q1; + m22 = p2 * q2; + /* advance pointers */ + ell += 2; + ex += 2; + Z11 += m11; + Z12 += m12; + Z21 += m21; + Z22 += m22; + /* end of inner loop */ + } + /* compute left-over iterations */ + j += 2; + for (; j > 0; j--) { + /* compute outer product and add it to the Z matrix */ + p1=ell[0]; + q1=ex[0]; + m11 = p1 * q1; + q2=ex[lskip1]; + m12 = p1 * q2; + p2=ell[lskip1]; + m21 = p2 * q1; + m22 = p2 * q2; + /* advance pointers */ + ell += 1; + ex += 1; + Z11 += m11; + Z12 += m12; + Z21 += m21; + Z22 += m22; + } + /* finish computing the X(i) block */ + Z11 = ex[0] - Z11; + ex[0] = Z11; + Z12 = ex[lskip1] - Z12; + ex[lskip1] = Z12; + p1 = ell[lskip1]; + Z21 = ex[1] - Z21 - p1*Z11; + ex[1] = Z21; + Z22 = ex[1+lskip1] - Z22 - p1*Z12; + ex[1+lskip1] = Z22; + /* end of outer loop */ + } +} + + +void dFactorLDLT (dReal *A, dReal *d, int n, int nskip1) +{ + int i,j; + dReal sum,*ell,*dee,dd,p1,p2,q1,q2,Z11,m11,Z21,m21,Z22,m22; + if (n < 1) return; + + for (i=0; i<=n-2; i += 2) { + /* solve L*(D*l)=a, l is scaled elements in 2 x i block at A(i,0) */ + dSolveL1_2 (A,A+i*nskip1,i,nskip1); + /* scale the elements in a 2 x i block at A(i,0), and also */ + /* compute Z = the outer product matrix that we'll need. */ + Z11 = 0; + Z21 = 0; + Z22 = 0; + ell = A+i*nskip1; + dee = d; + for (j=i-6; j >= 0; j -= 6) { + p1 = ell[0]; + p2 = ell[nskip1]; + dd = dee[0]; + q1 = p1*dd; + q2 = p2*dd; + ell[0] = q1; + ell[nskip1] = q2; + m11 = p1*q1; + m21 = p2*q1; + m22 = p2*q2; + Z11 += m11; + Z21 += m21; + Z22 += m22; + p1 = ell[1]; + p2 = ell[1+nskip1]; + dd = dee[1]; + q1 = p1*dd; + q2 = p2*dd; + ell[1] = q1; + ell[1+nskip1] = q2; + m11 = p1*q1; + m21 = p2*q1; + m22 = p2*q2; + Z11 += m11; + Z21 += m21; + Z22 += m22; + p1 = ell[2]; + p2 = ell[2+nskip1]; + dd = dee[2]; + q1 = p1*dd; + q2 = p2*dd; + ell[2] = q1; + ell[2+nskip1] = q2; + m11 = p1*q1; + m21 = p2*q1; + m22 = p2*q2; + Z11 += m11; + Z21 += m21; + Z22 += m22; + p1 = ell[3]; + p2 = ell[3+nskip1]; + dd = dee[3]; + q1 = p1*dd; + q2 = p2*dd; + ell[3] = q1; + ell[3+nskip1] = q2; + m11 = p1*q1; + m21 = p2*q1; + m22 = p2*q2; + Z11 += m11; + Z21 += m21; + Z22 += m22; + p1 = ell[4]; + p2 = ell[4+nskip1]; + dd = dee[4]; + q1 = p1*dd; + q2 = p2*dd; + ell[4] = q1; + ell[4+nskip1] = q2; + m11 = p1*q1; + m21 = p2*q1; + m22 = p2*q2; + Z11 += m11; + Z21 += m21; + Z22 += m22; + p1 = ell[5]; + p2 = ell[5+nskip1]; + dd = dee[5]; + q1 = p1*dd; + q2 = p2*dd; + ell[5] = q1; + ell[5+nskip1] = q2; + m11 = p1*q1; + m21 = p2*q1; + m22 = p2*q2; + Z11 += m11; + Z21 += m21; + Z22 += m22; + ell += 6; + dee += 6; + } + /* compute left-over iterations */ + j += 6; + for (; j > 0; j--) { + p1 = ell[0]; + p2 = ell[nskip1]; + dd = dee[0]; + q1 = p1*dd; + q2 = p2*dd; + ell[0] = q1; + ell[nskip1] = q2; + m11 = p1*q1; + m21 = p2*q1; + m22 = p2*q2; + Z11 += m11; + Z21 += m21; + Z22 += m22; + ell++; + dee++; + } + /* solve for diagonal 2 x 2 block at A(i,i) */ + Z11 = ell[0] - Z11; + Z21 = ell[nskip1] - Z21; + Z22 = ell[1+nskip1] - Z22; + dee = d + i; + /* factorize 2 x 2 block Z,dee */ + /* factorize row 1 */ + dee[0] = dRecip(Z11); + /* factorize row 2 */ + sum = 0; + q1 = Z21; + q2 = q1 * dee[0]; + Z21 = q2; + sum += q1*q2; + dee[1] = dRecip(Z22 - sum); + /* done factorizing 2 x 2 block */ + ell[nskip1] = Z21; + } + /* compute the (less than 2) rows at the bottom */ + switch (n-i) { + case 0: + break; + + case 1: + dSolveL1_1 (A,A+i*nskip1,i,nskip1); + /* scale the elements in a 1 x i block at A(i,0), and also */ + /* compute Z = the outer product matrix that we'll need. */ + Z11 = 0; + ell = A+i*nskip1; + dee = d; + for (j=i-6; j >= 0; j -= 6) { + p1 = ell[0]; + dd = dee[0]; + q1 = p1*dd; + ell[0] = q1; + m11 = p1*q1; + Z11 += m11; + p1 = ell[1]; + dd = dee[1]; + q1 = p1*dd; + ell[1] = q1; + m11 = p1*q1; + Z11 += m11; + p1 = ell[2]; + dd = dee[2]; + q1 = p1*dd; + ell[2] = q1; + m11 = p1*q1; + Z11 += m11; + p1 = ell[3]; + dd = dee[3]; + q1 = p1*dd; + ell[3] = q1; + m11 = p1*q1; + Z11 += m11; + p1 = ell[4]; + dd = dee[4]; + q1 = p1*dd; + ell[4] = q1; + m11 = p1*q1; + Z11 += m11; + p1 = ell[5]; + dd = dee[5]; + q1 = p1*dd; + ell[5] = q1; + m11 = p1*q1; + Z11 += m11; + ell += 6; + dee += 6; + } + /* compute left-over iterations */ + j += 6; + for (; j > 0; j--) { + p1 = ell[0]; + dd = dee[0]; + q1 = p1*dd; + ell[0] = q1; + m11 = p1*q1; + Z11 += m11; + ell++; + dee++; + } + /* solve for diagonal 1 x 1 block at A(i,i) */ + Z11 = ell[0] - Z11; + dee = d + i; + /* factorize 1 x 1 block Z,dee */ + /* factorize row 1 */ + dee[0] = dRecip(Z11); + /* done factorizing 1 x 1 block */ + break; + + default: *((char*)0)=0; /* this should never happen! */ + } +} diff --git a/HenocUniverse/ode/source/fastlsolve.c b/HenocUniverse/ode/source/fastlsolve.c new file mode 100755 index 0000000..0ae99d6 --- /dev/null +++ b/HenocUniverse/ode/source/fastlsolve.c @@ -0,0 +1,298 @@ +/* generated code, do not edit. */ + +#include "ode/matrix.h" + +/* solve L*X=B, with B containing 1 right hand sides. + * L is an n*n lower triangular matrix with ones on the diagonal. + * L is stored by rows and its leading dimension is lskip. + * B is an n*1 matrix that contains the right hand sides. + * B is stored by columns and its leading dimension is also lskip. + * B is overwritten with X. + * this processes blocks of 4*4. + * if this is in the factorizer source file, n must be a multiple of 4. + */ + +void dSolveL1 (const dReal *L, dReal *B, int n, int lskip1) +{ + /* declare variables - Z matrix, p and q vectors, etc */ + dReal Z11,Z21,Z31,Z41,p1,q1,p2,p3,p4,*ex; + const dReal *ell; + int lskip2,lskip3,i,j; + /* compute lskip values */ + lskip2 = 2*lskip1; + lskip3 = 3*lskip1; + /* compute all 4 x 1 blocks of X */ + for (i=0; i <= n-4; i+=4) { + /* compute all 4 x 1 block of X, from rows i..i+4-1 */ + /* set the Z matrix to 0 */ + Z11=0; + Z21=0; + Z31=0; + Z41=0; + ell = L + i*lskip1; + ex = B; + /* the inner loop that computes outer products and adds them to Z */ + for (j=i-12; j >= 0; j -= 12) { + /* load p and q values */ + p1=ell[0]; + q1=ex[0]; + p2=ell[lskip1]; + p3=ell[lskip2]; + p4=ell[lskip3]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + Z21 += p2 * q1; + Z31 += p3 * q1; + Z41 += p4 * q1; + /* load p and q values */ + p1=ell[1]; + q1=ex[1]; + p2=ell[1+lskip1]; + p3=ell[1+lskip2]; + p4=ell[1+lskip3]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + Z21 += p2 * q1; + Z31 += p3 * q1; + Z41 += p4 * q1; + /* load p and q values */ + p1=ell[2]; + q1=ex[2]; + p2=ell[2+lskip1]; + p3=ell[2+lskip2]; + p4=ell[2+lskip3]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + Z21 += p2 * q1; + Z31 += p3 * q1; + Z41 += p4 * q1; + /* load p and q values */ + p1=ell[3]; + q1=ex[3]; + p2=ell[3+lskip1]; + p3=ell[3+lskip2]; + p4=ell[3+lskip3]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + Z21 += p2 * q1; + Z31 += p3 * q1; + Z41 += p4 * q1; + /* load p and q values */ + p1=ell[4]; + q1=ex[4]; + p2=ell[4+lskip1]; + p3=ell[4+lskip2]; + p4=ell[4+lskip3]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + Z21 += p2 * q1; + Z31 += p3 * q1; + Z41 += p4 * q1; + /* load p and q values */ + p1=ell[5]; + q1=ex[5]; + p2=ell[5+lskip1]; + p3=ell[5+lskip2]; + p4=ell[5+lskip3]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + Z21 += p2 * q1; + Z31 += p3 * q1; + Z41 += p4 * q1; + /* load p and q values */ + p1=ell[6]; + q1=ex[6]; + p2=ell[6+lskip1]; + p3=ell[6+lskip2]; + p4=ell[6+lskip3]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + Z21 += p2 * q1; + Z31 += p3 * q1; + Z41 += p4 * q1; + /* load p and q values */ + p1=ell[7]; + q1=ex[7]; + p2=ell[7+lskip1]; + p3=ell[7+lskip2]; + p4=ell[7+lskip3]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + Z21 += p2 * q1; + Z31 += p3 * q1; + Z41 += p4 * q1; + /* load p and q values */ + p1=ell[8]; + q1=ex[8]; + p2=ell[8+lskip1]; + p3=ell[8+lskip2]; + p4=ell[8+lskip3]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + Z21 += p2 * q1; + Z31 += p3 * q1; + Z41 += p4 * q1; + /* load p and q values */ + p1=ell[9]; + q1=ex[9]; + p2=ell[9+lskip1]; + p3=ell[9+lskip2]; + p4=ell[9+lskip3]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + Z21 += p2 * q1; + Z31 += p3 * q1; + Z41 += p4 * q1; + /* load p and q values */ + p1=ell[10]; + q1=ex[10]; + p2=ell[10+lskip1]; + p3=ell[10+lskip2]; + p4=ell[10+lskip3]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + Z21 += p2 * q1; + Z31 += p3 * q1; + Z41 += p4 * q1; + /* load p and q values */ + p1=ell[11]; + q1=ex[11]; + p2=ell[11+lskip1]; + p3=ell[11+lskip2]; + p4=ell[11+lskip3]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + Z21 += p2 * q1; + Z31 += p3 * q1; + Z41 += p4 * q1; + /* advance pointers */ + ell += 12; + ex += 12; + /* end of inner loop */ + } + /* compute left-over iterations */ + j += 12; + for (; j > 0; j--) { + /* load p and q values */ + p1=ell[0]; + q1=ex[0]; + p2=ell[lskip1]; + p3=ell[lskip2]; + p4=ell[lskip3]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + Z21 += p2 * q1; + Z31 += p3 * q1; + Z41 += p4 * q1; + /* advance pointers */ + ell += 1; + ex += 1; + } + /* finish computing the X(i) block */ + Z11 = ex[0] - Z11; + ex[0] = Z11; + p1 = ell[lskip1]; + Z21 = ex[1] - Z21 - p1*Z11; + ex[1] = Z21; + p1 = ell[lskip2]; + p2 = ell[1+lskip2]; + Z31 = ex[2] - Z31 - p1*Z11 - p2*Z21; + ex[2] = Z31; + p1 = ell[lskip3]; + p2 = ell[1+lskip3]; + p3 = ell[2+lskip3]; + Z41 = ex[3] - Z41 - p1*Z11 - p2*Z21 - p3*Z31; + ex[3] = Z41; + /* end of outer loop */ + } + /* compute rows at end that are not a multiple of block size */ + for (; i < n; i++) { + /* compute all 1 x 1 block of X, from rows i..i+1-1 */ + /* set the Z matrix to 0 */ + Z11=0; + ell = L + i*lskip1; + ex = B; + /* the inner loop that computes outer products and adds them to Z */ + for (j=i-12; j >= 0; j -= 12) { + /* load p and q values */ + p1=ell[0]; + q1=ex[0]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + /* load p and q values */ + p1=ell[1]; + q1=ex[1]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + /* load p and q values */ + p1=ell[2]; + q1=ex[2]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + /* load p and q values */ + p1=ell[3]; + q1=ex[3]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + /* load p and q values */ + p1=ell[4]; + q1=ex[4]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + /* load p and q values */ + p1=ell[5]; + q1=ex[5]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + /* load p and q values */ + p1=ell[6]; + q1=ex[6]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + /* load p and q values */ + p1=ell[7]; + q1=ex[7]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + /* load p and q values */ + p1=ell[8]; + q1=ex[8]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + /* load p and q values */ + p1=ell[9]; + q1=ex[9]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + /* load p and q values */ + p1=ell[10]; + q1=ex[10]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + /* load p and q values */ + p1=ell[11]; + q1=ex[11]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + /* advance pointers */ + ell += 12; + ex += 12; + /* end of inner loop */ + } + /* compute left-over iterations */ + j += 12; + for (; j > 0; j--) { + /* load p and q values */ + p1=ell[0]; + q1=ex[0]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + /* advance pointers */ + ell += 1; + ex += 1; + } + /* finish computing the X(i) block */ + Z11 = ex[0] - Z11; + ex[0] = Z11; + } +} diff --git a/HenocUniverse/ode/source/fastltsolve.c b/HenocUniverse/ode/source/fastltsolve.c new file mode 100755 index 0000000..eb950f6 --- /dev/null +++ b/HenocUniverse/ode/source/fastltsolve.c @@ -0,0 +1,199 @@ +/* generated code, do not edit. */ + +#include "ode/matrix.h" + +/* solve L^T * x=b, with b containing 1 right hand side. + * L is an n*n lower triangular matrix with ones on the diagonal. + * L is stored by rows and its leading dimension is lskip. + * b is an n*1 matrix that contains the right hand side. + * b is overwritten with x. + * this processes blocks of 4. + */ + +void dSolveL1T (const dReal *L, dReal *B, int n, int lskip1) +{ + /* declare variables - Z matrix, p and q vectors, etc */ + dReal Z11,m11,Z21,m21,Z31,m31,Z41,m41,p1,q1,p2,p3,p4,*ex; + const dReal *ell; + int lskip2,lskip3,i,j; + /* special handling for L and B because we're solving L1 *transpose* */ + L = L + (n-1)*(lskip1+1); + B = B + n-1; + lskip1 = -lskip1; + /* compute lskip values */ + lskip2 = 2*lskip1; + lskip3 = 3*lskip1; + /* compute all 4 x 1 blocks of X */ + for (i=0; i <= n-4; i+=4) { + /* compute all 4 x 1 block of X, from rows i..i+4-1 */ + /* set the Z matrix to 0 */ + Z11=0; + Z21=0; + Z31=0; + Z41=0; + ell = L - i; + ex = B; + /* the inner loop that computes outer products and adds them to Z */ + for (j=i-4; j >= 0; j -= 4) { + /* load p and q values */ + p1=ell[0]; + q1=ex[0]; + p2=ell[-1]; + p3=ell[-2]; + p4=ell[-3]; + /* compute outer product and add it to the Z matrix */ + m11 = p1 * q1; + m21 = p2 * q1; + m31 = p3 * q1; + m41 = p4 * q1; + ell += lskip1; + Z11 += m11; + Z21 += m21; + Z31 += m31; + Z41 += m41; + /* load p and q values */ + p1=ell[0]; + q1=ex[-1]; + p2=ell[-1]; + p3=ell[-2]; + p4=ell[-3]; + /* compute outer product and add it to the Z matrix */ + m11 = p1 * q1; + m21 = p2 * q1; + m31 = p3 * q1; + m41 = p4 * q1; + ell += lskip1; + Z11 += m11; + Z21 += m21; + Z31 += m31; + Z41 += m41; + /* load p and q values */ + p1=ell[0]; + q1=ex[-2]; + p2=ell[-1]; + p3=ell[-2]; + p4=ell[-3]; + /* compute outer product and add it to the Z matrix */ + m11 = p1 * q1; + m21 = p2 * q1; + m31 = p3 * q1; + m41 = p4 * q1; + ell += lskip1; + Z11 += m11; + Z21 += m21; + Z31 += m31; + Z41 += m41; + /* load p and q values */ + p1=ell[0]; + q1=ex[-3]; + p2=ell[-1]; + p3=ell[-2]; + p4=ell[-3]; + /* compute outer product and add it to the Z matrix */ + m11 = p1 * q1; + m21 = p2 * q1; + m31 = p3 * q1; + m41 = p4 * q1; + ell += lskip1; + ex -= 4; + Z11 += m11; + Z21 += m21; + Z31 += m31; + Z41 += m41; + /* end of inner loop */ + } + /* compute left-over iterations */ + j += 4; + for (; j > 0; j--) { + /* load p and q values */ + p1=ell[0]; + q1=ex[0]; + p2=ell[-1]; + p3=ell[-2]; + p4=ell[-3]; + /* compute outer product and add it to the Z matrix */ + m11 = p1 * q1; + m21 = p2 * q1; + m31 = p3 * q1; + m41 = p4 * q1; + ell += lskip1; + ex -= 1; + Z11 += m11; + Z21 += m21; + Z31 += m31; + Z41 += m41; + } + /* finish computing the X(i) block */ + Z11 = ex[0] - Z11; + ex[0] = Z11; + p1 = ell[-1]; + Z21 = ex[-1] - Z21 - p1*Z11; + ex[-1] = Z21; + p1 = ell[-2]; + p2 = ell[-2+lskip1]; + Z31 = ex[-2] - Z31 - p1*Z11 - p2*Z21; + ex[-2] = Z31; + p1 = ell[-3]; + p2 = ell[-3+lskip1]; + p3 = ell[-3+lskip2]; + Z41 = ex[-3] - Z41 - p1*Z11 - p2*Z21 - p3*Z31; + ex[-3] = Z41; + /* end of outer loop */ + } + /* compute rows at end that are not a multiple of block size */ + for (; i < n; i++) { + /* compute all 1 x 1 block of X, from rows i..i+1-1 */ + /* set the Z matrix to 0 */ + Z11=0; + ell = L - i; + ex = B; + /* the inner loop that computes outer products and adds them to Z */ + for (j=i-4; j >= 0; j -= 4) { + /* load p and q values */ + p1=ell[0]; + q1=ex[0]; + /* compute outer product and add it to the Z matrix */ + m11 = p1 * q1; + ell += lskip1; + Z11 += m11; + /* load p and q values */ + p1=ell[0]; + q1=ex[-1]; + /* compute outer product and add it to the Z matrix */ + m11 = p1 * q1; + ell += lskip1; + Z11 += m11; + /* load p and q values */ + p1=ell[0]; + q1=ex[-2]; + /* compute outer product and add it to the Z matrix */ + m11 = p1 * q1; + ell += lskip1; + Z11 += m11; + /* load p and q values */ + p1=ell[0]; + q1=ex[-3]; + /* compute outer product and add it to the Z matrix */ + m11 = p1 * q1; + ell += lskip1; + ex -= 4; + Z11 += m11; + /* end of inner loop */ + } + /* compute left-over iterations */ + j += 4; + for (; j > 0; j--) { + /* load p and q values */ + p1=ell[0]; + q1=ex[0]; + /* compute outer product and add it to the Z matrix */ + m11 = p1 * q1; + ell += lskip1; + ex -= 1; + Z11 += m11; + } + /* finish computing the X(i) block */ + Z11 = ex[0] - Z11; + ex[0] = Z11; + } +} diff --git a/HenocUniverse/ode/source/joint.cpp b/HenocUniverse/ode/source/joint.cpp new file mode 100755 index 0000000..155a628 --- /dev/null +++ b/HenocUniverse/ode/source/joint.cpp @@ -0,0 +1,2691 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* + +design note: the general principle for giving a joint the option of connecting +to the static environment (i.e. the absolute frame) is to check the second +body (joint->node[1].body), and if it is zero then behave as if its body +transform is the identity. + +*/ + +#include +#include +#include +#include "joint.h" + +//**************************************************************************** +// externs + +extern "C" void dBodyAddTorque (dBodyID, dReal fx, dReal fy, dReal fz); +extern "C" void dBodyAddForce (dBodyID, dReal fx, dReal fy, dReal fz); + +//**************************************************************************** +// utility + +// set three "ball-and-socket" rows in the constraint equation, and the +// corresponding right hand side. + +static inline void setBall (dxJoint *joint, dxJoint::Info2 *info, + dVector3 anchor1, dVector3 anchor2) +{ + // anchor points in global coordinates with respect to body PORs. + dVector3 a1,a2; + + int s = info->rowskip; + + // set jacobian + info->J1l[0] = 1; + info->J1l[s+1] = 1; + info->J1l[2*s+2] = 1; + dMULTIPLY0_331 (a1,joint->node[0].body->R,anchor1); + dCROSSMAT (info->J1a,a1,s,-,+); + if (joint->node[1].body) { + info->J2l[0] = -1; + info->J2l[s+1] = -1; + info->J2l[2*s+2] = -1; + dMULTIPLY0_331 (a2,joint->node[1].body->R,anchor2); + dCROSSMAT (info->J2a,a2,s,+,-); + } + + // set right hand side + dReal k = info->fps * info->erp; + if (joint->node[1].body) { + for (int j=0; j<3; j++) { + info->c[j] = k * (a2[j] + joint->node[1].body->pos[j] - + a1[j] - joint->node[0].body->pos[j]); + } + } + else { + for (int j=0; j<3; j++) { + info->c[j] = k * (anchor2[j] - a1[j] - + joint->node[0].body->pos[j]); + } + } +} + + +// this is like setBall(), except that `axis' is a unit length vector +// (in global coordinates) that should be used for the first jacobian +// position row (the other two row vectors will be derived from this). +// `erp1' is the erp value to use along the axis. + +static inline void setBall2 (dxJoint *joint, dxJoint::Info2 *info, + dVector3 anchor1, dVector3 anchor2, + dVector3 axis, dReal erp1) +{ + // anchor points in global coordinates with respect to body PORs. + dVector3 a1,a2; + + int i,s = info->rowskip; + + // get vectors normal to the axis. in setBall() axis,q1,q2 is [1 0 0], + // [0 1 0] and [0 0 1], which makes everything much easier. + dVector3 q1,q2; + dPlaneSpace (axis,q1,q2); + + // set jacobian + for (i=0; i<3; i++) info->J1l[i] = axis[i]; + for (i=0; i<3; i++) info->J1l[s+i] = q1[i]; + for (i=0; i<3; i++) info->J1l[2*s+i] = q2[i]; + dMULTIPLY0_331 (a1,joint->node[0].body->R,anchor1); + dCROSS (info->J1a,=,a1,axis); + dCROSS (info->J1a+s,=,a1,q1); + dCROSS (info->J1a+2*s,=,a1,q2); + if (joint->node[1].body) { + for (i=0; i<3; i++) info->J2l[i] = -axis[i]; + for (i=0; i<3; i++) info->J2l[s+i] = -q1[i]; + for (i=0; i<3; i++) info->J2l[2*s+i] = -q2[i]; + dMULTIPLY0_331 (a2,joint->node[1].body->R,anchor2); + dCROSS (info->J2a,= -,a2,axis); + dCROSS (info->J2a+s,= -,a2,q1); + dCROSS (info->J2a+2*s,= -,a2,q2); + } + + // set right hand side - measure error along (axis,q1,q2) + dReal k1 = info->fps * erp1; + dReal k = info->fps * info->erp; + + for (i=0; i<3; i++) a1[i] += joint->node[0].body->pos[i]; + if (joint->node[1].body) { + for (i=0; i<3; i++) a2[i] += joint->node[1].body->pos[i]; + info->c[0] = k1 * (dDOT(axis,a2) - dDOT(axis,a1)); + info->c[1] = k * (dDOT(q1,a2) - dDOT(q1,a1)); + info->c[2] = k * (dDOT(q2,a2) - dDOT(q2,a1)); + } + else { + info->c[0] = k1 * (dDOT(axis,anchor2) - dDOT(axis,a1)); + info->c[1] = k * (dDOT(q1,anchor2) - dDOT(q1,a1)); + info->c[2] = k * (dDOT(q2,anchor2) - dDOT(q2,a1)); + } +} + + +// set three orientation rows in the constraint equation, and the +// corresponding right hand side. + +static void setFixedOrientation(dxJoint *joint, dxJoint::Info2 *info, dQuaternion qrel, int start_row) +{ + int s = info->rowskip; + int start_index = start_row * s; + + // 3 rows to make body rotations equal + info->J1a[start_index] = 1; + info->J1a[start_index + s + 1] = 1; + info->J1a[start_index + s*2+2] = 1; + if (joint->node[1].body) { + info->J2a[start_index] = -1; + info->J2a[start_index + s+1] = -1; + info->J2a[start_index + s*2+2] = -1; + } + + // compute the right hand side. the first three elements will result in + // relative angular velocity of the two bodies - this is set to bring them + // back into alignment. the correcting angular velocity is + // |angular_velocity| = angle/time = erp*theta / stepsize + // = (erp*fps) * theta + // angular_velocity = |angular_velocity| * u + // = (erp*fps) * theta * u + // where rotation along unit length axis u by theta brings body 2's frame + // to qrel with respect to body 1's frame. using a small angle approximation + // for sin(), this gives + // angular_velocity = (erp*fps) * 2 * v + // where the quaternion of the relative rotation between the two bodies is + // q = [cos(theta/2) sin(theta/2)*u] = [s v] + + // get qerr = relative rotation (rotation error) between two bodies + dQuaternion qerr,e; + if (joint->node[1].body) { + dQuaternion qq; + dQMultiply1 (qq,joint->node[0].body->q,joint->node[1].body->q); + dQMultiply2 (qerr,qq,qrel); + } + else { + dQMultiply3 (qerr,joint->node[0].body->q,qrel); + } + if (qerr[0] < 0) { + qerr[1] = -qerr[1]; // adjust sign of qerr to make theta small + qerr[2] = -qerr[2]; + qerr[3] = -qerr[3]; + } + dMULTIPLY0_331 (e,joint->node[0].body->R,qerr+1); // @@@ bad SIMD padding! + dReal k = info->fps * info->erp; + info->c[start_row] = 2*k * e[0]; + info->c[start_row+1] = 2*k * e[1]; + info->c[start_row+2] = 2*k * e[2]; +} + + +// compute anchor points relative to bodies + +static void setAnchors (dxJoint *j, dReal x, dReal y, dReal z, + dVector3 anchor1, dVector3 anchor2) +{ + if (j->node[0].body) { + dReal q[4]; + q[0] = x - j->node[0].body->pos[0]; + q[1] = y - j->node[0].body->pos[1]; + q[2] = z - j->node[0].body->pos[2]; + q[3] = 0; + dMULTIPLY1_331 (anchor1,j->node[0].body->R,q); + if (j->node[1].body) { + q[0] = x - j->node[1].body->pos[0]; + q[1] = y - j->node[1].body->pos[1]; + q[2] = z - j->node[1].body->pos[2]; + q[3] = 0; + dMULTIPLY1_331 (anchor2,j->node[1].body->R,q); + } + else { + anchor2[0] = x; + anchor2[1] = y; + anchor2[2] = z; + } + } + anchor1[3] = 0; + anchor2[3] = 0; +} + + +// compute axes relative to bodies. either axis1 or axis2 can be 0. + +static void setAxes (dxJoint *j, dReal x, dReal y, dReal z, + dVector3 axis1, dVector3 axis2) +{ + if (j->node[0].body) { + dReal q[4]; + q[0] = x; + q[1] = y; + q[2] = z; + q[3] = 0; + dNormalize3 (q); + if (axis1) { + dMULTIPLY1_331 (axis1,j->node[0].body->R,q); + axis1[3] = 0; + } + if (axis2) { + if (j->node[1].body) { + dMULTIPLY1_331 (axis2,j->node[1].body->R,q); + } + else { + axis2[0] = x; + axis2[1] = y; + axis2[2] = z; + } + axis2[3] = 0; + } + } +} + + +static void getAnchor (dxJoint *j, dVector3 result, dVector3 anchor1) +{ + if (j->node[0].body) { + dMULTIPLY0_331 (result,j->node[0].body->R,anchor1); + result[0] += j->node[0].body->pos[0]; + result[1] += j->node[0].body->pos[1]; + result[2] += j->node[0].body->pos[2]; + } +} + + +static void getAnchor2 (dxJoint *j, dVector3 result, dVector3 anchor2) +{ + if (j->node[1].body) { + dMULTIPLY0_331 (result,j->node[1].body->R,anchor2); + result[0] += j->node[1].body->pos[0]; + result[1] += j->node[1].body->pos[1]; + result[2] += j->node[1].body->pos[2]; + } + else { + result[0] = anchor2[0]; + result[1] = anchor2[1]; + result[2] = anchor2[2]; + } +} + + +static void getAxis (dxJoint *j, dVector3 result, dVector3 axis1) +{ + if (j->node[0].body) { + dMULTIPLY0_331 (result,j->node[0].body->R,axis1); + } +} + + +static void getAxis2 (dxJoint *j, dVector3 result, dVector3 axis2) +{ + if (j->node[1].body) { + dMULTIPLY0_331 (result,j->node[1].body->R,axis2); + } + else { + result[0] = axis2[0]; + result[1] = axis2[1]; + result[2] = axis2[2]; + } +} + + +static dReal getHingeAngleFromRelativeQuat (dQuaternion qrel, dVector3 axis) +{ + // the angle between the two bodies is extracted from the quaternion that + // represents the relative rotation between them. recall that a quaternion + // q is: + // [s,v] = [ cos(theta/2) , sin(theta/2) * u ] + // where s is a scalar and v is a 3-vector. u is a unit length axis and + // theta is a rotation along that axis. we can get theta/2 by: + // theta/2 = atan2 ( sin(theta/2) , cos(theta/2) ) + // but we can't get sin(theta/2) directly, only its absolute value, i.e.: + // |v| = |sin(theta/2)| * |u| + // = |sin(theta/2)| + // using this value will have a strange effect. recall that there are two + // quaternion representations of a given rotation, q and -q. typically as + // a body rotates along the axis it will go through a complete cycle using + // one representation and then the next cycle will use the other + // representation. this corresponds to u pointing in the direction of the + // hinge axis and then in the opposite direction. the result is that theta + // will appear to go "backwards" every other cycle. here is a fix: if u + // points "away" from the direction of the hinge (motor) axis (i.e. more + // than 90 degrees) then use -q instead of q. this represents the same + // rotation, but results in the cos(theta/2) value being sign inverted. + + // extract the angle from the quaternion. cost2 = cos(theta/2), + // sint2 = |sin(theta/2)| + dReal cost2 = qrel[0]; + dReal sint2 = dSqrt (qrel[1]*qrel[1]+qrel[2]*qrel[2]+qrel[3]*qrel[3]); + dReal theta = (dDOT(qrel+1,axis) >= 0) ? // @@@ padding assumptions + (2 * dAtan2(sint2,cost2)) : // if u points in direction of axis + (2 * dAtan2(sint2,-cost2)); // if u points in opposite direction + + // the angle we get will be between 0..2*pi, but we want to return angles + // between -pi..pi + if (theta > M_PI) theta -= 2*M_PI; + + // the angle we've just extracted has the wrong sign + theta = -theta; + + return theta; +} + + +// given two bodies (body1,body2), the hinge axis that they are connected by +// w.r.t. body1 (axis), and the initial relative orientation between them +// (q_initial), return the relative rotation angle. the initial relative +// orientation corresponds to an angle of zero. if body2 is 0 then measure the +// angle between body1 and the static frame. +// +// this will not return the correct angle if the bodies rotate along any axis +// other than the given hinge axis. + +static dReal getHingeAngle (dxBody *body1, dxBody *body2, dVector3 axis, + dQuaternion q_initial) +{ + // get qrel = relative rotation between the two bodies + dQuaternion qrel; + if (body2) { + dQuaternion qq; + dQMultiply1 (qq,body1->q,body2->q); + dQMultiply2 (qrel,qq,q_initial); + } + else { + // pretend body2->q is the identity + dQMultiply3 (qrel,body1->q,q_initial); + } + + return getHingeAngleFromRelativeQuat (qrel,axis); +} + +//**************************************************************************** +// dxJointLimitMotor + +void dxJointLimitMotor::init (dxWorld *world) +{ + vel = 0; + fmax = 0; + lostop = -dInfinity; + histop = dInfinity; + fudge_factor = 1; + normal_cfm = world->global_cfm; + stop_erp = world->global_erp; + stop_cfm = world->global_cfm; + bounce = 0; + limit = 0; + limit_err = 0; +} + + +void dxJointLimitMotor::set (int num, dReal value) +{ + switch (num) { + case dParamLoStop: + if (value <= histop) lostop = value; + break; + case dParamHiStop: + if (value >= lostop) histop = value; + break; + case dParamVel: + vel = value; + break; + case dParamFMax: + if (value >= 0) fmax = value; + break; + case dParamFudgeFactor: + if (value >= 0 && value <= 1) fudge_factor = value; + break; + case dParamBounce: + bounce = value; + break; + case dParamCFM: + normal_cfm = value; + break; + case dParamStopERP: + stop_erp = value; + break; + case dParamStopCFM: + stop_cfm = value; + break; + } +} + + +dReal dxJointLimitMotor::get (int num) +{ + switch (num) { + case dParamLoStop: return lostop; + case dParamHiStop: return histop; + case dParamVel: return vel; + case dParamFMax: return fmax; + case dParamFudgeFactor: return fudge_factor; + case dParamBounce: return bounce; + case dParamCFM: return normal_cfm; + case dParamStopERP: return stop_erp; + case dParamStopCFM: return stop_cfm; + default: return 0; + } +} + + +int dxJointLimitMotor::testRotationalLimit (dReal angle) +{ + if (angle <= lostop) { + limit = 1; + limit_err = angle - lostop; + return 1; + } + else if (angle >= histop) { + limit = 2; + limit_err = angle - histop; + return 1; + } + else { + limit = 0; + return 0; + } +} + + +int dxJointLimitMotor::addLimot (dxJoint *joint, + dxJoint::Info2 *info, int row, + dVector3 ax1, int rotational) +{ + int srow = row * info->rowskip; + + // if the joint is powered, or has joint limits, add in the extra row + int powered = fmax > 0; + if (powered || limit) { + dReal *J1 = rotational ? info->J1a : info->J1l; + dReal *J2 = rotational ? info->J2a : info->J2l; + + J1[srow+0] = ax1[0]; + J1[srow+1] = ax1[1]; + J1[srow+2] = ax1[2]; + if (joint->node[1].body) { + J2[srow+0] = -ax1[0]; + J2[srow+1] = -ax1[1]; + J2[srow+2] = -ax1[2]; + } + + // linear limot torque decoupling step: + // + // if this is a linear limot (e.g. from a slider), we have to be careful + // that the linear constraint forces (+/- ax1) applied to the two bodies + // do not create a torque couple. in other words, the points that the + // constraint force is applied at must lie along the same ax1 axis. + // a torque couple will result in powered or limited slider-jointed free + // bodies from gaining angular momentum. + // the solution used here is to apply the constraint forces at the point + // halfway between the body centers. there is no penalty (other than an + // extra tiny bit of computation) in doing this adjustment. note that we + // only need to do this if the constraint connects two bodies. + + dVector3 ltd; // Linear Torque Decoupling vector (a torque) + if (!rotational && joint->node[1].body) { + dVector3 c; + c[0]=REAL(0.5)*(joint->node[1].body->pos[0]-joint->node[0].body->pos[0]); + c[1]=REAL(0.5)*(joint->node[1].body->pos[1]-joint->node[0].body->pos[1]); + c[2]=REAL(0.5)*(joint->node[1].body->pos[2]-joint->node[0].body->pos[2]); + dCROSS (ltd,=,c,ax1); + info->J1a[srow+0] = ltd[0]; + info->J1a[srow+1] = ltd[1]; + info->J1a[srow+2] = ltd[2]; + info->J2a[srow+0] = ltd[0]; + info->J2a[srow+1] = ltd[1]; + info->J2a[srow+2] = ltd[2]; + } + + // if we're limited low and high simultaneously, the joint motor is + // ineffective + if (limit && (lostop == histop)) powered = 0; + + if (powered) { + info->cfm[row] = normal_cfm; + if (! limit) { + info->c[row] = vel; + info->lo[row] = -fmax; + info->hi[row] = fmax; + } + else { + // the joint is at a limit, AND is being powered. if the joint is + // being powered into the limit then we apply the maximum motor force + // in that direction, because the motor is working against the + // immovable limit. if the joint is being powered away from the limit + // then we have problems because actually we need *two* lcp + // constraints to handle this case. so we fake it and apply some + // fraction of the maximum force. the fraction to use can be set as + // a fudge factor. + + dReal fm = fmax; + if (vel > 0) fm = -fm; + + // if we're powering away from the limit, apply the fudge factor + if ((limit==1 && vel > 0) || (limit==2 && vel < 0)) fm *= fudge_factor; + + if (rotational) { + dBodyAddTorque (joint->node[0].body,-fm*ax1[0],-fm*ax1[1], + -fm*ax1[2]); + if (joint->node[1].body) + dBodyAddTorque (joint->node[1].body,fm*ax1[0],fm*ax1[1],fm*ax1[2]); + } + else { + dBodyAddForce (joint->node[0].body,-fm*ax1[0],-fm*ax1[1],-fm*ax1[2]); + if (joint->node[1].body) { + dBodyAddForce (joint->node[1].body,fm*ax1[0],fm*ax1[1],fm*ax1[2]); + + // linear limot torque decoupling step: refer to above discussion + dBodyAddTorque (joint->node[0].body,-fm*ltd[0],-fm*ltd[1], + -fm*ltd[2]); + dBodyAddTorque (joint->node[1].body,-fm*ltd[0],-fm*ltd[1], + -fm*ltd[2]); + } + } + } + } + + if (limit) { + dReal k = info->fps * stop_erp; + info->c[row] = -k * limit_err; + info->cfm[row] = stop_cfm; + + if (lostop == histop) { + // limited low and high simultaneously + info->lo[row] = -dInfinity; + info->hi[row] = dInfinity; + } + else { + if (limit == 1) { + // low limit + info->lo[row] = 0; + info->hi[row] = dInfinity; + } + else { + // high limit + info->lo[row] = -dInfinity; + info->hi[row] = 0; + } + + // deal with bounce + if (bounce > 0) { + // calculate joint velocity + dReal vel; + if (rotational) { + vel = dDOT(joint->node[0].body->avel,ax1); + if (joint->node[1].body) + vel -= dDOT(joint->node[1].body->avel,ax1); + } + else { + vel = dDOT(joint->node[0].body->lvel,ax1); + if (joint->node[1].body) + vel -= dDOT(joint->node[1].body->lvel,ax1); + } + + // only apply bounce if the velocity is incoming, and if the + // resulting c[] exceeds what we already have. + if (limit == 1) { + // low limit + if (vel < 0) { + dReal newc = -bounce * vel; + if (newc > info->c[row]) info->c[row] = newc; + } + } + else { + // high limit - all those computations are reversed + if (vel > 0) { + dReal newc = -bounce * vel; + if (newc < info->c[row]) info->c[row] = newc; + } + } + } + } + } + return 1; + } + else return 0; +} + +//**************************************************************************** +// ball and socket + +static void ballInit (dxJointBall *j) +{ + dSetZero (j->anchor1,4); + dSetZero (j->anchor2,4); +} + + +static void ballGetInfo1 (dxJointBall *j, dxJoint::Info1 *info) +{ + info->m = 3; + info->nub = 3; +} + + +static void ballGetInfo2 (dxJointBall *joint, dxJoint::Info2 *info) +{ + setBall (joint,info,joint->anchor1,joint->anchor2); +} + + +extern "C" void dJointSetBallAnchor (dxJointBall *joint, + dReal x, dReal y, dReal z) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__dball_vtable,"joint is not a ball"); + setAnchors (joint,x,y,z,joint->anchor1,joint->anchor2); +} + + +extern "C" void dJointGetBallAnchor (dxJointBall *joint, dVector3 result) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(result,"bad result argument"); + dUASSERT(joint->vtable == &__dball_vtable,"joint is not a ball"); + if (joint->flags & dJOINT_REVERSE) + getAnchor2 (joint,result,joint->anchor2); + else + getAnchor (joint,result,joint->anchor1); +} + + +extern "C" void dJointGetBallAnchor2 (dxJointBall *joint, dVector3 result) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(result,"bad result argument"); + dUASSERT(joint->vtable == &__dball_vtable,"joint is not a ball"); + if (joint->flags & dJOINT_REVERSE) + getAnchor (joint,result,joint->anchor1); + else + getAnchor2 (joint,result,joint->anchor2); +} + + +dxJoint::Vtable __dball_vtable = { + sizeof(dxJointBall), + (dxJoint::init_fn*) ballInit, + (dxJoint::getInfo1_fn*) ballGetInfo1, + (dxJoint::getInfo2_fn*) ballGetInfo2, + dJointTypeBall}; + +//**************************************************************************** +// hinge + +static void hingeInit (dxJointHinge *j) +{ + dSetZero (j->anchor1,4); + dSetZero (j->anchor2,4); + dSetZero (j->axis1,4); + j->axis1[0] = 1; + dSetZero (j->axis2,4); + j->axis2[0] = 1; + dSetZero (j->qrel,4); + j->limot.init (j->world); +} + + +static void hingeGetInfo1 (dxJointHinge *j, dxJoint::Info1 *info) +{ + info->nub = 5; + + // see if joint is powered + if (j->limot.fmax > 0) + info->m = 6; // powered hinge needs an extra constraint row + else info->m = 5; + + // see if we're at a joint limit. + if ((j->limot.lostop >= -M_PI || j->limot.histop <= M_PI) && + j->limot.lostop <= j->limot.histop) { + dReal angle = getHingeAngle (j->node[0].body,j->node[1].body,j->axis1, + j->qrel); + if (j->limot.testRotationalLimit (angle)) info->m = 6; + } +} + + +static void hingeGetInfo2 (dxJointHinge *joint, dxJoint::Info2 *info) +{ + // set the three ball-and-socket rows + setBall (joint,info,joint->anchor1,joint->anchor2); + + // set the two hinge rows. the hinge axis should be the only unconstrained + // rotational axis, the angular velocity of the two bodies perpendicular to + // the hinge axis should be equal. thus the constraint equations are + // p*w1 - p*w2 = 0 + // q*w1 - q*w2 = 0 + // where p and q are unit vectors normal to the hinge axis, and w1 and w2 + // are the angular velocity vectors of the two bodies. + + dVector3 ax1; // length 1 joint axis in global coordinates, from 1st body + dVector3 p,q; // plane space vectors for ax1 + dMULTIPLY0_331 (ax1,joint->node[0].body->R,joint->axis1); + dPlaneSpace (ax1,p,q); + + int s3=3*info->rowskip; + int s4=4*info->rowskip; + + info->J1a[s3+0] = p[0]; + info->J1a[s3+1] = p[1]; + info->J1a[s3+2] = p[2]; + info->J1a[s4+0] = q[0]; + info->J1a[s4+1] = q[1]; + info->J1a[s4+2] = q[2]; + + if (joint->node[1].body) { + info->J2a[s3+0] = -p[0]; + info->J2a[s3+1] = -p[1]; + info->J2a[s3+2] = -p[2]; + info->J2a[s4+0] = -q[0]; + info->J2a[s4+1] = -q[1]; + info->J2a[s4+2] = -q[2]; + } + + // compute the right hand side of the constraint equation. set relative + // body velocities along p and q to bring the hinge back into alignment. + // if ax1,ax2 are the unit length hinge axes as computed from body1 and + // body2, we need to rotate both bodies along the axis u = (ax1 x ax2). + // if `theta' is the angle between ax1 and ax2, we need an angular velocity + // along u to cover angle erp*theta in one step : + // |angular_velocity| = angle/time = erp*theta / stepsize + // = (erp*fps) * theta + // angular_velocity = |angular_velocity| * (ax1 x ax2) / |ax1 x ax2| + // = (erp*fps) * theta * (ax1 x ax2) / sin(theta) + // ...as ax1 and ax2 are unit length. if theta is smallish, + // theta ~= sin(theta), so + // angular_velocity = (erp*fps) * (ax1 x ax2) + // ax1 x ax2 is in the plane space of ax1, so we project the angular + // velocity to p and q to find the right hand side. + + dVector3 ax2,b; + if (joint->node[1].body) { + dMULTIPLY0_331 (ax2,joint->node[1].body->R,joint->axis2); + } + else { + ax2[0] = joint->axis2[0]; + ax2[1] = joint->axis2[1]; + ax2[2] = joint->axis2[2]; + } + dCROSS (b,=,ax1,ax2); + dReal k = info->fps * info->erp; + info->c[3] = k * dDOT(b,p); + info->c[4] = k * dDOT(b,q); + + // if the hinge is powered, or has joint limits, add in the stuff + joint->limot.addLimot (joint,info,5,ax1,1); +} + + +// compute initial relative rotation body1 -> body2, or env -> body1 + +static void hingeComputeInitialRelativeRotation (dxJointHinge *joint) +{ + if (joint->node[0].body) { + if (joint->node[1].body) { + dQMultiply1 (joint->qrel,joint->node[0].body->q,joint->node[1].body->q); + } + else { + // set joint->qrel to the transpose of the first body q + joint->qrel[0] = joint->node[0].body->q[0]; + for (int i=1; i<4; i++) joint->qrel[i] = -joint->node[0].body->q[i]; + } + } +} + + +extern "C" void dJointSetHingeAnchor (dxJointHinge *joint, + dReal x, dReal y, dReal z) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__dhinge_vtable,"joint is not a hinge"); + setAnchors (joint,x,y,z,joint->anchor1,joint->anchor2); + hingeComputeInitialRelativeRotation (joint); +} + + +extern "C" void dJointSetHingeAxis (dxJointHinge *joint, + dReal x, dReal y, dReal z) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__dhinge_vtable,"joint is not a hinge"); + setAxes (joint,x,y,z,joint->axis1,joint->axis2); + hingeComputeInitialRelativeRotation (joint); +} + + +extern "C" void dJointGetHingeAnchor (dxJointHinge *joint, dVector3 result) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(result,"bad result argument"); + dUASSERT(joint->vtable == &__dhinge_vtable,"joint is not a hinge"); + if (joint->flags & dJOINT_REVERSE) + getAnchor2 (joint,result,joint->anchor2); + else + getAnchor (joint,result,joint->anchor1); +} + + +extern "C" void dJointGetHingeAnchor2 (dxJointHinge *joint, dVector3 result) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(result,"bad result argument"); + dUASSERT(joint->vtable == &__dhinge_vtable,"joint is not a hinge"); + if (joint->flags & dJOINT_REVERSE) + getAnchor (joint,result,joint->anchor1); + else + getAnchor2 (joint,result,joint->anchor2); +} + + +extern "C" void dJointGetHingeAxis (dxJointHinge *joint, dVector3 result) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(result,"bad result argument"); + dUASSERT(joint->vtable == &__dhinge_vtable,"joint is not a hinge"); + getAxis (joint,result,joint->axis1); +} + + +extern "C" void dJointSetHingeParam (dxJointHinge *joint, + int parameter, dReal value) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__dhinge_vtable,"joint is not a hinge"); + joint->limot.set (parameter,value); +} + + +extern "C" dReal dJointGetHingeParam (dxJointHinge *joint, int parameter) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__dhinge_vtable,"joint is not a hinge"); + return joint->limot.get (parameter); +} + + +extern "C" dReal dJointGetHingeAngle (dxJointHinge *joint) +{ + dAASSERT(joint); + dUASSERT(joint->vtable == &__dhinge_vtable,"joint is not a hinge"); + if (joint->node[0].body) { + dReal ang = getHingeAngle (joint->node[0].body,joint->node[1].body,joint->axis1, + joint->qrel); + if (joint->flags & dJOINT_REVERSE) + return -ang; + else + return ang; + } + else return 0; +} + + +extern "C" dReal dJointGetHingeAngleRate (dxJointHinge *joint) +{ + dAASSERT(joint); + dUASSERT(joint->vtable == &__dhinge_vtable,"joint is not a Hinge"); + if (joint->node[0].body) { + dVector3 axis; + dMULTIPLY0_331 (axis,joint->node[0].body->R,joint->axis1); + dReal rate = dDOT(axis,joint->node[0].body->avel); + if (joint->node[1].body) rate -= dDOT(axis,joint->node[1].body->avel); + if (joint->flags & dJOINT_REVERSE) rate = - rate; + return rate; + } + else return 0; +} + + +extern "C" void dJointAddHingeTorque (dxJointHinge *joint, dReal torque) +{ + dVector3 axis; + dAASSERT(joint); + dUASSERT(joint->vtable == &__dhinge_vtable,"joint is not a Hinge"); + + if (joint->flags & dJOINT_REVERSE) + torque = -torque; + + getAxis (joint,axis,joint->axis1); + axis[0] *= torque; + axis[1] *= torque; + axis[2] *= torque; + + if (joint->node[0].body != 0) + dBodyAddTorque (joint->node[0].body, axis[0], axis[1], axis[2]); + if (joint->node[1].body != 0) + dBodyAddTorque(joint->node[1].body, -axis[0], -axis[1], -axis[2]); +} + + +dxJoint::Vtable __dhinge_vtable = { + sizeof(dxJointHinge), + (dxJoint::init_fn*) hingeInit, + (dxJoint::getInfo1_fn*) hingeGetInfo1, + (dxJoint::getInfo2_fn*) hingeGetInfo2, + dJointTypeHinge}; + +//**************************************************************************** +// slider + +static void sliderInit (dxJointSlider *j) +{ + dSetZero (j->axis1,4); + j->axis1[0] = 1; + dSetZero (j->qrel,4); + dSetZero (j->offset,4); + j->limot.init (j->world); +} + + +extern "C" dReal dJointGetSliderPosition (dxJointSlider *joint) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__dslider_vtable,"joint is not a slider"); + + // get axis1 in global coordinates + dVector3 ax1,q; + dMULTIPLY0_331 (ax1,joint->node[0].body->R,joint->axis1); + + if (joint->node[1].body) { + // get body2 + offset point in global coordinates + dMULTIPLY0_331 (q,joint->node[1].body->R,joint->offset); + for (int i=0; i<3; i++) q[i] = joint->node[0].body->pos[i] - q[i] - + joint->node[1].body->pos[i]; + } + else { + for (int i=0; i<3; i++) q[i] = joint->node[0].body->pos[i] - + joint->offset[i]; + + } + return dDOT(ax1,q); +} + + +extern "C" dReal dJointGetSliderPositionRate (dxJointSlider *joint) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__dslider_vtable,"joint is not a slider"); + + // get axis1 in global coordinates + dVector3 ax1; + dMULTIPLY0_331 (ax1,joint->node[0].body->R,joint->axis1); + + if (joint->node[1].body) { + return dDOT(ax1,joint->node[0].body->lvel) - + dDOT(ax1,joint->node[1].body->lvel); + } + else { + return dDOT(ax1,joint->node[0].body->lvel); + } +} + + +static void sliderGetInfo1 (dxJointSlider *j, dxJoint::Info1 *info) +{ + info->nub = 5; + + // see if joint is powered + if (j->limot.fmax > 0) + info->m = 6; // powered slider needs an extra constraint row + else info->m = 5; + + // see if we're at a joint limit. + j->limot.limit = 0; + if ((j->limot.lostop > -dInfinity || j->limot.histop < dInfinity) && + j->limot.lostop <= j->limot.histop) { + // measure joint position + dReal pos = dJointGetSliderPosition (j); + if (pos <= j->limot.lostop) { + j->limot.limit = 1; + j->limot.limit_err = pos - j->limot.lostop; + info->m = 6; + } + else if (pos >= j->limot.histop) { + j->limot.limit = 2; + j->limot.limit_err = pos - j->limot.histop; + info->m = 6; + } + } +} + + +static void sliderGetInfo2 (dxJointSlider *joint, dxJoint::Info2 *info) +{ + int i,s = info->rowskip; + int s3=3*s,s4=4*s; + + // pull out pos and R for both bodies. also get the `connection' + // vector pos2-pos1. + + dReal *pos1,*pos2,*R1,*R2; + dVector3 c; + pos1 = joint->node[0].body->pos; + R1 = joint->node[0].body->R; + if (joint->node[1].body) { + pos2 = joint->node[1].body->pos; + R2 = joint->node[1].body->R; + for (i=0; i<3; i++) c[i] = pos2[i] - pos1[i]; + } + else { + pos2 = 0; + R2 = 0; + } + + // 3 rows to make body rotations equal + setFixedOrientation(joint, info, joint->qrel, 0); + + // remaining two rows. we want: vel2 = vel1 + w1 x c ... but this would + // result in three equations, so we project along the planespace vectors + // so that sliding along the slider axis is disregarded. for symmetry we + // also substitute (w1+w2)/2 for w1, as w1 is supposed to equal w2. + + dVector3 ax1; // joint axis in global coordinates (unit length) + dVector3 p,q; // plane space of ax1 + dMULTIPLY0_331 (ax1,R1,joint->axis1); + dPlaneSpace (ax1,p,q); + if (joint->node[1].body) { + dVector3 tmp; + dCROSS (tmp, = REAL(0.5) * ,c,p); + for (i=0; i<3; i++) info->J2a[s3+i] = tmp[i]; + for (i=0; i<3; i++) info->J2a[s3+i] = tmp[i]; + dCROSS (tmp, = REAL(0.5) * ,c,q); + for (i=0; i<3; i++) info->J2a[s4+i] = tmp[i]; + for (i=0; i<3; i++) info->J2a[s4+i] = tmp[i]; + for (i=0; i<3; i++) info->J2l[s3+i] = -p[i]; + for (i=0; i<3; i++) info->J2l[s4+i] = -q[i]; + } + for (i=0; i<3; i++) info->J1l[s3+i] = p[i]; + for (i=0; i<3; i++) info->J1l[s4+i] = q[i]; + + // compute last two elements of right hand side. we want to align the offset + // point (in body 2's frame) with the center of body 1. + dReal k = info->fps * info->erp; + if (joint->node[1].body) { + dVector3 ofs; // offset point in global coordinates + dMULTIPLY0_331 (ofs,R2,joint->offset); + for (i=0; i<3; i++) c[i] += ofs[i]; + info->c[3] = k * dDOT(p,c); + info->c[4] = k * dDOT(q,c); + } + else { + dVector3 ofs; // offset point in global coordinates + for (i=0; i<3; i++) ofs[i] = joint->offset[i] - pos1[i]; + info->c[3] = k * dDOT(p,ofs); + info->c[4] = k * dDOT(q,ofs); + } + + // if the slider is powered, or has joint limits, add in the extra row + joint->limot.addLimot (joint,info,5,ax1,0); +} + + +extern "C" void dJointSetSliderAxis (dxJointSlider *joint, + dReal x, dReal y, dReal z) +{ + int i; + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__dslider_vtable,"joint is not a slider"); + setAxes (joint,x,y,z,joint->axis1,0); + + // compute initial relative rotation body1 -> body2, or env -> body1 + // also compute center of body1 w.r.t body 2 + if (joint->node[1].body) { + dQMultiply1 (joint->qrel,joint->node[0].body->q,joint->node[1].body->q); + dVector3 c; + for (i=0; i<3; i++) + c[i] = joint->node[0].body->pos[i] - joint->node[1].body->pos[i]; + dMULTIPLY1_331 (joint->offset,joint->node[1].body->R,c); + } + else { + // set joint->qrel to the transpose of the first body's q + joint->qrel[0] = joint->node[0].body->q[0]; + for (i=1; i<4; i++) joint->qrel[i] = -joint->node[0].body->q[i]; + for (i=0; i<3; i++) joint->offset[i] = joint->node[0].body->pos[i]; + } +} + + +extern "C" void dJointGetSliderAxis (dxJointSlider *joint, dVector3 result) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(result,"bad result argument"); + dUASSERT(joint->vtable == &__dslider_vtable,"joint is not a slider"); + getAxis (joint,result,joint->axis1); +} + + +extern "C" void dJointSetSliderParam (dxJointSlider *joint, + int parameter, dReal value) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__dslider_vtable,"joint is not a slider"); + joint->limot.set (parameter,value); +} + + +extern "C" dReal dJointGetSliderParam (dxJointSlider *joint, int parameter) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__dslider_vtable,"joint is not a slider"); + return joint->limot.get (parameter); +} + + +extern "C" void dJointAddSliderForce (dxJointSlider *joint, dReal force) +{ + dVector3 axis; + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__dslider_vtable,"joint is not a slider"); + + if (joint->flags & dJOINT_REVERSE) + force -= force; + + getAxis (joint,axis,joint->axis1); + axis[0] *= force; + axis[1] *= force; + axis[2] *= force; + + if (joint->node[0].body != 0) + dBodyAddForce (joint->node[0].body,axis[0],axis[1],axis[2]); + if (joint->node[1].body != 0) + dBodyAddForce(joint->node[1].body, -axis[0], -axis[1], -axis[2]); +} + + +dxJoint::Vtable __dslider_vtable = { + sizeof(dxJointSlider), + (dxJoint::init_fn*) sliderInit, + (dxJoint::getInfo1_fn*) sliderGetInfo1, + (dxJoint::getInfo2_fn*) sliderGetInfo2, + dJointTypeSlider}; + +//**************************************************************************** +// contact + +static void contactInit (dxJointContact *j) +{ + // default frictionless contact. hmmm, this info gets overwritten straight + // away anyway, so why bother? +#if 0 /* so don't bother ;) */ + j->contact.surface.mode = 0; + j->contact.surface.mu = 0; + dSetZero (j->contact.geom.pos,4); + dSetZero (j->contact.geom.normal,4); + j->contact.geom.depth = 0; +#endif +} + + +static void contactGetInfo1 (dxJointContact *j, dxJoint::Info1 *info) +{ + // make sure mu's >= 0, then calculate number of constraint rows and number + // of unbounded rows. + int m = 1, nub=0; + if (j->contact.surface.mu < 0) j->contact.surface.mu = 0; + if (j->contact.surface.mode & dContactMu2) { + if (j->contact.surface.mu > 0) m++; + if (j->contact.surface.mu2 < 0) j->contact.surface.mu2 = 0; + if (j->contact.surface.mu2 > 0) m++; + if (j->contact.surface.mu == dInfinity) nub ++; + if (j->contact.surface.mu2 == dInfinity) nub ++; + } + else { + if (j->contact.surface.mu > 0) m += 2; + if (j->contact.surface.mu == dInfinity) nub += 2; + } + + j->the_m = m; + info->m = m; + info->nub = nub; +} + + +static void contactGetInfo2 (dxJointContact *j, dxJoint::Info2 *info) +{ + int i,s = info->rowskip; + int s2 = 2*s; + + // get normal, with sign adjusted for body1/body2 polarity + dVector3 normal; + if (j->flags & dJOINT_REVERSE) { + normal[0] = - j->contact.geom.normal[0]; + normal[1] = - j->contact.geom.normal[1]; + normal[2] = - j->contact.geom.normal[2]; + } + else { + normal[0] = j->contact.geom.normal[0]; + normal[1] = j->contact.geom.normal[1]; + normal[2] = j->contact.geom.normal[2]; + } + normal[3] = 0; // @@@ hmmm + + // c1,c2 = contact points with respect to body PORs + dVector3 c1,c2; + for (i=0; i<3; i++) c1[i] = j->contact.geom.pos[i] - j->node[0].body->pos[i]; + + // set jacobian for normal + info->J1l[0] = normal[0]; + info->J1l[1] = normal[1]; + info->J1l[2] = normal[2]; + dCROSS (info->J1a,=,c1,normal); + if (j->node[1].body) { + for (i=0; i<3; i++) c2[i] = j->contact.geom.pos[i] - + j->node[1].body->pos[i]; + info->J2l[0] = -normal[0]; + info->J2l[1] = -normal[1]; + info->J2l[2] = -normal[2]; + dCROSS (info->J2a,= -,c2,normal); + } + + // set right hand side and cfm value for normal + dReal erp = info->erp; + if (j->contact.surface.mode & dContactSoftERP) + erp = j->contact.surface.soft_erp; + dReal k = info->fps * erp; + dReal depth = j->contact.geom.depth - j->world->contactp.min_depth; + if (depth < 0) depth = 0; + dReal maxvel = j->world->contactp.max_vel; + if (k*depth > maxvel) info->c[0] = maxvel; else info->c[0] = k*depth; + if (j->contact.surface.mode & dContactSoftCFM) + info->cfm[0] = j->contact.surface.soft_cfm; + + // deal with bounce + if (j->contact.surface.mode & dContactBounce) { + // calculate outgoing velocity (-ve for incoming contact) + dReal outgoing = dDOT(info->J1l,j->node[0].body->lvel) + + dDOT(info->J1a,j->node[0].body->avel); + if (j->node[1].body) { + outgoing += dDOT(info->J2l,j->node[1].body->lvel) + + dDOT(info->J2a,j->node[1].body->avel); + } + // only apply bounce if the outgoing velocity is greater than the + // threshold, and if the resulting c[0] exceeds what we already have. + if (j->contact.surface.bounce_vel >= 0 && + (-outgoing) > j->contact.surface.bounce_vel) { + dReal newc = - j->contact.surface.bounce * outgoing; + if (newc > info->c[0]) info->c[0] = newc; + } + } + + // set LCP limits for normal + info->lo[0] = 0; + info->hi[0] = dInfinity; + + // now do jacobian for tangential forces + dVector3 t1,t2; // two vectors tangential to normal + + // first friction direction + if (j->the_m >= 2) { + if (j->contact.surface.mode & dContactFDir1) { // use fdir1 ? + t1[0] = j->contact.fdir1[0]; + t1[1] = j->contact.fdir1[1]; + t1[2] = j->contact.fdir1[2]; + dCROSS (t2,=,normal,t1); + } + else { + dPlaneSpace (normal,t1,t2); + } + info->J1l[s+0] = t1[0]; + info->J1l[s+1] = t1[1]; + info->J1l[s+2] = t1[2]; + dCROSS (info->J1a+s,=,c1,t1); + if (j->node[1].body) { + info->J2l[s+0] = -t1[0]; + info->J2l[s+1] = -t1[1]; + info->J2l[s+2] = -t1[2]; + dCROSS (info->J2a+s,= -,c2,t1); + } + // set right hand side + if (j->contact.surface.mode & dContactMotion1) { + info->c[1] = j->contact.surface.motion1; + } + // set LCP bounds and friction index. this depends on the approximation + // mode + info->lo[1] = -j->contact.surface.mu; + info->hi[1] = j->contact.surface.mu; + if (j->contact.surface.mode & dContactApprox1_1) info->findex[1] = 0; + + // set slip (constraint force mixing) + if (j->contact.surface.mode & dContactSlip1) + info->cfm[1] = j->contact.surface.slip1; + } + + // second friction direction + if (j->the_m >= 3) { + info->J1l[s2+0] = t2[0]; + info->J1l[s2+1] = t2[1]; + info->J1l[s2+2] = t2[2]; + dCROSS (info->J1a+s2,=,c1,t2); + if (j->node[1].body) { + info->J2l[s2+0] = -t2[0]; + info->J2l[s2+1] = -t2[1]; + info->J2l[s2+2] = -t2[2]; + dCROSS (info->J2a+s2,= -,c2,t2); + } + // set right hand side + if (j->contact.surface.mode & dContactMotion2) { + info->c[2] = j->contact.surface.motion2; + } + // set LCP bounds and friction index. this depends on the approximation + // mode + if (j->contact.surface.mode & dContactMu2) { + info->lo[2] = -j->contact.surface.mu2; + info->hi[2] = j->contact.surface.mu2; + } + else { + info->lo[2] = -j->contact.surface.mu; + info->hi[2] = j->contact.surface.mu; + } + if (j->contact.surface.mode & dContactApprox1_2) info->findex[2] = 0; + + // set slip (constraint force mixing) + if (j->contact.surface.mode & dContactSlip2) + info->cfm[2] = j->contact.surface.slip2; + } +} + + +dxJoint::Vtable __dcontact_vtable = { + sizeof(dxJointContact), + (dxJoint::init_fn*) contactInit, + (dxJoint::getInfo1_fn*) contactGetInfo1, + (dxJoint::getInfo2_fn*) contactGetInfo2, + dJointTypeContact}; + +//**************************************************************************** +// hinge 2. note that this joint must be attached to two bodies for it to work + +static dReal measureHinge2Angle (dxJointHinge2 *joint) +{ + dVector3 a1,a2; + dMULTIPLY0_331 (a1,joint->node[1].body->R,joint->axis2); + dMULTIPLY1_331 (a2,joint->node[0].body->R,a1); + dReal x = dDOT(joint->v1,a2); + dReal y = dDOT(joint->v2,a2); + return -dAtan2 (y,x); +} + + +static void hinge2Init (dxJointHinge2 *j) +{ + dSetZero (j->anchor1,4); + dSetZero (j->anchor2,4); + dSetZero (j->axis1,4); + j->axis1[0] = 1; + dSetZero (j->axis2,4); + j->axis2[1] = 1; + j->c0 = 0; + j->s0 = 0; + + dSetZero (j->v1,4); + j->v1[0] = 1; + dSetZero (j->v2,4); + j->v2[1] = 1; + + j->limot1.init (j->world); + j->limot2.init (j->world); + + j->susp_erp = j->world->global_erp; + j->susp_cfm = j->world->global_cfm; + + j->flags |= dJOINT_TWOBODIES; +} + + +static void hinge2GetInfo1 (dxJointHinge2 *j, dxJoint::Info1 *info) +{ + info->m = 4; + info->nub = 4; + + // see if we're powered or at a joint limit for axis 1 + int atlimit=0; + if ((j->limot1.lostop >= -M_PI || j->limot1.histop <= M_PI) && + j->limot1.lostop <= j->limot1.histop) { + dReal angle = measureHinge2Angle (j); + if (j->limot1.testRotationalLimit (angle)) atlimit = 1; + } + if (atlimit || j->limot1.fmax > 0) info->m++; + + // see if we're powering axis 2 (we currently never limit this axis) + j->limot2.limit = 0; + if (j->limot2.fmax > 0) info->m++; +} + + +// macro that computes ax1,ax2 = axis 1 and 2 in global coordinates (they are +// relative to body 1 and 2 initially) and then computes the constrained +// rotational axis as the cross product of ax1 and ax2. +// the sin and cos of the angle between axis 1 and 2 is computed, this comes +// from dot and cross product rules. + +#define HINGE2_GET_AXIS_INFO(axis,sin_angle,cos_angle) \ + dVector3 ax1,ax2; \ + dMULTIPLY0_331 (ax1,joint->node[0].body->R,joint->axis1); \ + dMULTIPLY0_331 (ax2,joint->node[1].body->R,joint->axis2); \ + dCROSS (axis,=,ax1,ax2); \ + sin_angle = dSqrt (axis[0]*axis[0] + axis[1]*axis[1] + axis[2]*axis[2]); \ + cos_angle = dDOT (ax1,ax2); + + +static void hinge2GetInfo2 (dxJointHinge2 *joint, dxJoint::Info2 *info) +{ + // get information we need to set the hinge row + dReal s,c; + dVector3 q; + HINGE2_GET_AXIS_INFO (q,s,c); + dNormalize3 (q); // @@@ quicker: divide q by s ? + + // set the three ball-and-socket rows (aligned to the suspension axis ax1) + setBall2 (joint,info,joint->anchor1,joint->anchor2,ax1,joint->susp_erp); + + // set the hinge row + int s3=3*info->rowskip; + info->J1a[s3+0] = q[0]; + info->J1a[s3+1] = q[1]; + info->J1a[s3+2] = q[2]; + if (joint->node[1].body) { + info->J2a[s3+0] = -q[0]; + info->J2a[s3+1] = -q[1]; + info->J2a[s3+2] = -q[2]; + } + + // compute the right hand side for the constrained rotational DOF. + // axis 1 and axis 2 are separated by an angle `theta'. the desired + // separation angle is theta0. sin(theta0) and cos(theta0) are recorded + // in the joint structure. the correcting angular velocity is: + // |angular_velocity| = angle/time = erp*(theta0-theta) / stepsize + // = (erp*fps) * (theta0-theta) + // (theta0-theta) can be computed using the following small-angle-difference + // approximation: + // theta0-theta ~= tan(theta0-theta) + // = sin(theta0-theta)/cos(theta0-theta) + // = (c*s0 - s*c0) / (c*c0 + s*s0) + // = c*s0 - s*c0 assuming c*c0 + s*s0 ~= 1 + // where c = cos(theta), s = sin(theta) + // c0 = cos(theta0), s0 = sin(theta0) + + dReal k = info->fps * info->erp; + info->c[3] = k * (joint->c0 * s - joint->s0 * c); + + // if the axis1 hinge is powered, or has joint limits, add in more stuff + int row = 4 + joint->limot1.addLimot (joint,info,4,ax1,1); + + // if the axis2 hinge is powered, add in more stuff + joint->limot2.addLimot (joint,info,row,ax2,1); + + // set parameter for the suspension + info->cfm[0] = joint->susp_cfm; +} + + +// compute vectors v1 and v2 (embedded in body1), used to measure angle +// between body 1 and body 2 + +static void makeHinge2V1andV2 (dxJointHinge2 *joint) +{ + if (joint->node[0].body) { + // get axis 1 and 2 in global coords + dVector3 ax1,ax2,v; + dMULTIPLY0_331 (ax1,joint->node[0].body->R,joint->axis1); + dMULTIPLY0_331 (ax2,joint->node[1].body->R,joint->axis2); + + // don't do anything if the axis1 or axis2 vectors are zero or the same + if ((ax1[0]==0 && ax1[1]==0 && ax1[2]==0) || + (ax2[0]==0 && ax2[1]==0 && ax2[2]==0) || + (ax1[0]==ax2[0] && ax1[1]==ax2[1] && ax1[2]==ax2[2])) return; + + // modify axis 2 so it's perpendicular to axis 1 + dReal k = dDOT(ax1,ax2); + for (int i=0; i<3; i++) ax2[i] -= k*ax1[i]; + dNormalize3 (ax2); + + // make v1 = modified axis2, v2 = axis1 x (modified axis2) + dCROSS (v,=,ax1,ax2); + dMULTIPLY1_331 (joint->v1,joint->node[0].body->R,ax2); + dMULTIPLY1_331 (joint->v2,joint->node[0].body->R,v); + } +} + + +extern "C" void dJointSetHinge2Anchor (dxJointHinge2 *joint, + dReal x, dReal y, dReal z) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__dhinge2_vtable,"joint is not a hinge2"); + setAnchors (joint,x,y,z,joint->anchor1,joint->anchor2); + makeHinge2V1andV2 (joint); +} + + +extern "C" void dJointSetHinge2Axis1 (dxJointHinge2 *joint, + dReal x, dReal y, dReal z) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__dhinge2_vtable,"joint is not a hinge2"); + if (joint->node[0].body) { + dReal q[4]; + q[0] = x; + q[1] = y; + q[2] = z; + q[3] = 0; + dNormalize3 (q); + dMULTIPLY1_331 (joint->axis1,joint->node[0].body->R,q); + joint->axis1[3] = 0; + + // compute the sin and cos of the angle between axis 1 and axis 2 + dVector3 ax; + HINGE2_GET_AXIS_INFO(ax,joint->s0,joint->c0); + } + makeHinge2V1andV2 (joint); +} + + +extern "C" void dJointSetHinge2Axis2 (dxJointHinge2 *joint, + dReal x, dReal y, dReal z) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__dhinge2_vtable,"joint is not a hinge2"); + if (joint->node[1].body) { + dReal q[4]; + q[0] = x; + q[1] = y; + q[2] = z; + q[3] = 0; + dNormalize3 (q); + dMULTIPLY1_331 (joint->axis2,joint->node[1].body->R,q); + joint->axis1[3] = 0; + + // compute the sin and cos of the angle between axis 1 and axis 2 + dVector3 ax; + HINGE2_GET_AXIS_INFO(ax,joint->s0,joint->c0); + } + makeHinge2V1andV2 (joint); +} + + +extern "C" void dJointSetHinge2Param (dxJointHinge2 *joint, + int parameter, dReal value) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__dhinge2_vtable,"joint is not a hinge2"); + if ((parameter & 0xff00) == 0x100) { + joint->limot2.set (parameter & 0xff,value); + } + else { + if (parameter == dParamSuspensionERP) joint->susp_erp = value; + else if (parameter == dParamSuspensionCFM) joint->susp_cfm = value; + else joint->limot1.set (parameter,value); + } +} + + +extern "C" void dJointGetHinge2Anchor (dxJointHinge2 *joint, dVector3 result) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(result,"bad result argument"); + dUASSERT(joint->vtable == &__dhinge2_vtable,"joint is not a hinge2"); + if (joint->flags & dJOINT_REVERSE) + getAnchor2 (joint,result,joint->anchor2); + else + getAnchor (joint,result,joint->anchor1); +} + + +extern "C" void dJointGetHinge2Anchor2 (dxJointHinge2 *joint, dVector3 result) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(result,"bad result argument"); + dUASSERT(joint->vtable == &__dhinge2_vtable,"joint is not a hinge2"); + if (joint->flags & dJOINT_REVERSE) + getAnchor (joint,result,joint->anchor1); + else + getAnchor2 (joint,result,joint->anchor2); +} + + +extern "C" void dJointGetHinge2Axis1 (dxJointHinge2 *joint, dVector3 result) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(result,"bad result argument"); + dUASSERT(joint->vtable == &__dhinge2_vtable,"joint is not a hinge2"); + if (joint->node[0].body) { + dMULTIPLY0_331 (result,joint->node[0].body->R,joint->axis1); + } +} + + +extern "C" void dJointGetHinge2Axis2 (dxJointHinge2 *joint, dVector3 result) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(result,"bad result argument"); + dUASSERT(joint->vtable == &__dhinge2_vtable,"joint is not a hinge2"); + if (joint->node[1].body) { + dMULTIPLY0_331 (result,joint->node[1].body->R,joint->axis2); + } +} + + +extern "C" dReal dJointGetHinge2Param (dxJointHinge2 *joint, int parameter) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__dhinge2_vtable,"joint is not a hinge2"); + if ((parameter & 0xff00) == 0x100) { + return joint->limot2.get (parameter & 0xff); + } + else { + if (parameter == dParamSuspensionERP) return joint->susp_erp; + else if (parameter == dParamSuspensionCFM) return joint->susp_cfm; + else return joint->limot1.get (parameter); + } +} + + +extern "C" dReal dJointGetHinge2Angle1 (dxJointHinge2 *joint) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__dhinge2_vtable,"joint is not a hinge2"); + if (joint->node[0].body) return measureHinge2Angle (joint); + else return 0; +} + + +extern "C" dReal dJointGetHinge2Angle1Rate (dxJointHinge2 *joint) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__dhinge2_vtable,"joint is not a hinge2"); + if (joint->node[0].body) { + dVector3 axis; + dMULTIPLY0_331 (axis,joint->node[0].body->R,joint->axis1); + dReal rate = dDOT(axis,joint->node[0].body->avel); + if (joint->node[1].body) rate -= dDOT(axis,joint->node[1].body->avel); + return rate; + } + else return 0; +} + + +extern "C" dReal dJointGetHinge2Angle2Rate (dxJointHinge2 *joint) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__dhinge2_vtable,"joint is not a hinge2"); + if (joint->node[0].body && joint->node[1].body) { + dVector3 axis; + dMULTIPLY0_331 (axis,joint->node[1].body->R,joint->axis2); + dReal rate = dDOT(axis,joint->node[0].body->avel); + if (joint->node[1].body) rate -= dDOT(axis,joint->node[1].body->avel); + return rate; + } + else return 0; +} + + +extern "C" void dJointAddHinge2Torques (dxJointHinge2 *joint, dReal torque1, dReal torque2) +{ + dVector3 axis1, axis2; + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__dhinge2_vtable,"joint is not a hinge2"); + + if (joint->node[0].body && joint->node[1].body) { + dMULTIPLY0_331 (axis1,joint->node[0].body->R,joint->axis1); + dMULTIPLY0_331 (axis2,joint->node[1].body->R,joint->axis2); + axis1[0] = axis1[0] * torque1 + axis2[0] * torque2; + axis1[1] = axis1[1] * torque1 + axis2[1] * torque2; + axis1[2] = axis1[2] * torque1 + axis2[2] * torque2; + dBodyAddTorque (joint->node[0].body,axis1[0],axis1[1],axis1[2]); + dBodyAddTorque(joint->node[1].body, -axis1[0], -axis1[1], -axis1[2]); + } +} + + +dxJoint::Vtable __dhinge2_vtable = { + sizeof(dxJointHinge2), + (dxJoint::init_fn*) hinge2Init, + (dxJoint::getInfo1_fn*) hinge2GetInfo1, + (dxJoint::getInfo2_fn*) hinge2GetInfo2, + dJointTypeHinge2}; + +//**************************************************************************** +// universal + +// I just realized that the universal joint is equivalent to a hinge 2 joint with +// perfectly stiff suspension. By comparing the hinge 2 implementation to +// the universal implementation, you may be able to improve this +// implementation (or, less likely, the hinge2 implementation). + +static void universalInit (dxJointUniversal *j) +{ + dSetZero (j->anchor1,4); + dSetZero (j->anchor2,4); + dSetZero (j->axis1,4); + j->axis1[0] = 1; + dSetZero (j->axis2,4); + j->axis2[1] = 1; + dSetZero(j->qrel1,4); + dSetZero(j->qrel2,4); + j->limot1.init (j->world); + j->limot2.init (j->world); +} + + +static void getUniversalAxes(dxJointUniversal *joint, dVector3 ax1, dVector3 ax2) +{ + // This says "ax1 = joint->node[0].body->R * joint->axis1" + dMULTIPLY0_331 (ax1,joint->node[0].body->R,joint->axis1); + + if (joint->node[1].body) { + dMULTIPLY0_331 (ax2,joint->node[1].body->R,joint->axis2); + } + else { + ax2[0] = joint->axis2[0]; + ax2[1] = joint->axis2[1]; + ax2[2] = joint->axis2[2]; + } +} + + +static dReal getUniversalAngle1(dxJointUniversal *joint) +{ + if (joint->node[0].body) { + // length 1 joint axis in global coordinates, from each body + dVector3 ax1, ax2; + dMatrix3 R; + dQuaternion qcross, qq, qrel; + + getUniversalAxes (joint,ax1,ax2); + + // It should be possible to get both angles without explicitly + // constructing the rotation matrix of the cross. Basically, + // orientation of the cross about axis1 comes from body 2, + // about axis 2 comes from body 1, and the perpendicular + // axis can come from the two bodies somehow. (We don't really + // want to assume it's 90 degrees, because in general the + // constraints won't be perfectly satisfied, or even very well + // satisfied.) + // + // However, we'd need a version of getHingeAngleFromRElativeQuat() + // that CAN handle when its relative quat is rotated along a direction + // other than the given axis. What I have here works, + // although it's probably much slower than need be. + + dRFrom2Axes(R, ax1[0], ax1[1], ax1[2], ax2[0], ax2[1], ax2[2]); + dRtoQ (R,qcross); + + // This code is essential the same as getHingeAngle(), see the comments + // there for details. + + // get qrel = relative rotation between node[0] and the cross + dQMultiply1 (qq,joint->node[0].body->q,qcross); + dQMultiply2 (qrel,qq,joint->qrel1); + + return getHingeAngleFromRelativeQuat(qrel, joint->axis1); + } + return 0; +} + + +static dReal getUniversalAngle2(dxJointUniversal *joint) +{ + if (joint->node[0].body) { + // length 1 joint axis in global coordinates, from each body + dVector3 ax1, ax2; + dMatrix3 R; + dQuaternion qcross, qq, qrel; + + getUniversalAxes (joint,ax1,ax2); + + // It should be possible to get both angles without explicitly + // constructing the rotation matrix of the cross. Basically, + // orientation of the cross about axis1 comes from body 2, + // about axis 2 comes from body 1, and the perpendicular + // axis can come from the two bodies somehow. (We don't really + // want to assume it's 90 degrees, because in general the + // constraints won't be perfectly satisfied, or even very well + // satisfied.) + // + // However, we'd need a version of getHingeAngleFromRElativeQuat() + // that CAN handle when its relative quat is rotated along a direction + // other than the given axis. What I have here works, + // although it's probably much slower than need be. + + dRFrom2Axes(R, ax2[0], ax2[1], ax2[2], ax1[0], ax1[1], ax1[2]); + dRtoQ(R, qcross); + + if (joint->node[1].body) { + dQMultiply1 (qq, joint->node[1].body->q, qcross); + dQMultiply2 (qrel,qq,joint->qrel2); + } + else { + // pretend joint->node[1].body->q is the identity + dQMultiply2 (qrel,qcross, joint->qrel2); + } + + return - getHingeAngleFromRelativeQuat(qrel, joint->axis2); + } + return 0; +} + + +static void universalGetInfo1 (dxJointUniversal *j, dxJoint::Info1 *info) +{ + info->nub = 4; + info->m = 4; + + // see if we're powered or at a joint limit. + bool constraint1 = j->limot1.fmax > 0; + bool constraint2 = j->limot2.fmax > 0; + + bool limiting1 = (j->limot1.lostop >= -M_PI || j->limot1.histop <= M_PI) && + j->limot1.lostop <= j->limot1.histop; + bool limiting2 = (j->limot2.lostop >= -M_PI || j->limot2.histop <= M_PI) && + j->limot2.lostop <= j->limot2.histop; + + // We need to call testRotationLimit() even if we're motored, since it + // records the result. + if (limiting1 || limiting2) { + dReal angle1, angle2; + angle1 = getUniversalAngle1(j); + angle2 = getUniversalAngle2(j); + if (limiting1 && j->limot1.testRotationalLimit (angle1)) constraint1 = true; + if (limiting2 && j->limot2.testRotationalLimit (angle2)) constraint2 = true; + } + if (constraint1) + info->m++; + if (constraint2) + info->m++; +} + + +static void universalGetInfo2 (dxJointUniversal *joint, dxJoint::Info2 *info) +{ + // set the three ball-and-socket rows + setBall (joint,info,joint->anchor1,joint->anchor2); + + // set the universal joint row. the angular velocity about an axis + // perpendicular to both joint axes should be equal. thus the constraint + // equation is + // p*w1 - p*w2 = 0 + // where p is a vector normal to both joint axes, and w1 and w2 + // are the angular velocity vectors of the two bodies. + + // length 1 joint axis in global coordinates, from each body + dVector3 ax1, ax2; + dVector3 ax2_temp; + // length 1 vector perpendicular to ax1 and ax2. Neither body can rotate + // about this. + dVector3 p; + dReal k; + + getUniversalAxes(joint, ax1, ax2); + k = dDOT(ax1, ax2); + ax2_temp[0] = ax2[0] - k*ax1[0]; + ax2_temp[1] = ax2[1] - k*ax1[1]; + ax2_temp[2] = ax2[2] - k*ax1[2]; + dCROSS(p, =, ax1, ax2_temp); + dNormalize3(p); + + int s3=3*info->rowskip; + + info->J1a[s3+0] = p[0]; + info->J1a[s3+1] = p[1]; + info->J1a[s3+2] = p[2]; + + if (joint->node[1].body) { + info->J2a[s3+0] = -p[0]; + info->J2a[s3+1] = -p[1]; + info->J2a[s3+2] = -p[2]; + } + + // compute the right hand side of the constraint equation. set relative + // body velocities along p to bring the axes back to perpendicular. + // If ax1, ax2 are unit length joint axes as computed from body1 and + // body2, we need to rotate both bodies along the axis p. If theta + // is the angle between ax1 and ax2, we need an angular velocity + // along p to cover the angle erp * (theta - Pi/2) in one step: + // + // |angular_velocity| = angle/time = erp*(theta - Pi/2) / stepsize + // = (erp*fps) * (theta - Pi/2) + // + // if theta is close to Pi/2, + // theta - Pi/2 ~= cos(theta), so + // |angular_velocity| ~= (erp*fps) * (ax1 dot ax2) + + info->c[3] = info->fps * info->erp * - dDOT(ax1, ax2); + + // if the first angle is powered, or has joint limits, add in the stuff + int row = 4 + joint->limot1.addLimot (joint,info,4,ax1,1); + + // if the second angle is powered, or has joint limits, add in more stuff + joint->limot2.addLimot (joint,info,row,ax2,1); +} + + +static void universalComputeInitialRelativeRotations (dxJointUniversal *joint) +{ + if (joint->node[0].body) { + dVector3 ax1, ax2; + dMatrix3 R; + dQuaternion qcross; + + getUniversalAxes(joint, ax1, ax2); + + // Axis 1. + dRFrom2Axes(R, ax1[0], ax1[1], ax1[2], ax2[0], ax2[1], ax2[2]); + dRtoQ(R, qcross); + dQMultiply1 (joint->qrel1, joint->node[0].body->q, qcross); + + // Axis 2. + dRFrom2Axes(R, ax2[0], ax2[1], ax2[2], ax1[0], ax1[1], ax1[2]); + dRtoQ(R, qcross); + if (joint->node[1].body) { + dQMultiply1 (joint->qrel2, joint->node[1].body->q, qcross); + } + else { + // set joint->qrel to qcross + for (int i=0; i<4; i++) joint->qrel2[i] = qcross[i]; + } + } +} + + +extern "C" void dJointSetUniversalAnchor (dxJointUniversal *joint, + dReal x, dReal y, dReal z) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__duniversal_vtable,"joint is not a universal"); + setAnchors (joint,x,y,z,joint->anchor1,joint->anchor2); + universalComputeInitialRelativeRotations(joint); +} + + +extern "C" void dJointSetUniversalAxis1 (dxJointUniversal *joint, + dReal x, dReal y, dReal z) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__duniversal_vtable,"joint is not a universal"); + if (joint->flags & dJOINT_REVERSE) + setAxes (joint,x,y,z,NULL,joint->axis2); + else + setAxes (joint,x,y,z,joint->axis1,NULL); + universalComputeInitialRelativeRotations(joint); +} + + +extern "C" void dJointSetUniversalAxis2 (dxJointUniversal *joint, + dReal x, dReal y, dReal z) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__duniversal_vtable,"joint is not a universal"); + if (joint->flags & dJOINT_REVERSE) + setAxes (joint,x,y,z,joint->axis1,NULL); + else + setAxes (joint,x,y,z,NULL,joint->axis2); + universalComputeInitialRelativeRotations(joint); +} + + +extern "C" void dJointGetUniversalAnchor (dxJointUniversal *joint, + dVector3 result) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(result,"bad result argument"); + dUASSERT(joint->vtable == &__duniversal_vtable,"joint is not a universal"); + if (joint->flags & dJOINT_REVERSE) + getAnchor2 (joint,result,joint->anchor2); + else + getAnchor (joint,result,joint->anchor1); +} + + +extern "C" void dJointGetUniversalAnchor2 (dxJointUniversal *joint, + dVector3 result) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(result,"bad result argument"); + dUASSERT(joint->vtable == &__duniversal_vtable,"joint is not a universal"); + if (joint->flags & dJOINT_REVERSE) + getAnchor (joint,result,joint->anchor1); + else + getAnchor2 (joint,result,joint->anchor2); +} + + +extern "C" void dJointGetUniversalAxis1 (dxJointUniversal *joint, + dVector3 result) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(result,"bad result argument"); + dUASSERT(joint->vtable == &__duniversal_vtable,"joint is not a universal"); + if (joint->flags & dJOINT_REVERSE) + getAxis2 (joint,result,joint->axis2); + else + getAxis (joint,result,joint->axis1); +} + + +extern "C" void dJointGetUniversalAxis2 (dxJointUniversal *joint, + dVector3 result) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(result,"bad result argument"); + dUASSERT(joint->vtable == &__duniversal_vtable,"joint is not a universal"); + if (joint->flags & dJOINT_REVERSE) + getAxis (joint,result,joint->axis1); + else + getAxis2 (joint,result,joint->axis2); +} + + +extern "C" void dJointSetUniversalParam (dxJointUniversal *joint, + int parameter, dReal value) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__duniversal_vtable,"joint is not a universal"); + if ((parameter & 0xff00) == 0x100) { + joint->limot2.set (parameter & 0xff,value); + } + else { + joint->limot1.set (parameter,value); + } +} + + +extern "C" dReal dJointGetUniversalParam (dxJointUniversal *joint, int parameter) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__duniversal_vtable,"joint is not a universal"); + if ((parameter & 0xff00) == 0x100) { + return joint->limot2.get (parameter & 0xff); + } + else { + return joint->limot1.get (parameter); + } +} + + +extern "C" dReal dJointGetUniversalAngle1 (dxJointUniversal *joint) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__duniversal_vtable,"joint is not a universal"); + if (joint->flags & dJOINT_REVERSE) + return getUniversalAngle2 (joint); + else + return getUniversalAngle1 (joint); +} + + +extern "C" dReal dJointGetUniversalAngle2 (dxJointUniversal *joint) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__duniversal_vtable,"joint is not a universal"); + if (joint->flags & dJOINT_REVERSE) + return getUniversalAngle1 (joint); + else + return getUniversalAngle2 (joint); +} + + +extern "C" dReal dJointGetUniversalAngle1Rate (dxJointUniversal *joint) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__duniversal_vtable,"joint is not a universal"); + + if (joint->node[0].body) { + dVector3 axis; + + if (joint->flags & dJOINT_REVERSE) + getAxis2 (joint,axis,joint->axis2); + else + getAxis (joint,axis,joint->axis1); + + dReal rate = dDOT(axis, joint->node[0].body->avel); + if (joint->node[1].body) rate -= dDOT(axis, joint->node[1].body->avel); + return rate; + } + return 0; +} + + +extern "C" dReal dJointGetUniversalAngle2Rate (dxJointUniversal *joint) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__duniversal_vtable,"joint is not a universal"); + + if (joint->node[0].body) { + dVector3 axis; + + if (joint->flags & dJOINT_REVERSE) + getAxis (joint,axis,joint->axis1); + else + getAxis2 (joint,axis,joint->axis2); + + dReal rate = dDOT(axis, joint->node[0].body->avel); + if (joint->node[1].body) rate -= dDOT(axis, joint->node[1].body->avel); + return rate; + } + return 0; +} + + +extern "C" void dJointAddUniversalTorques (dxJointUniversal *joint, dReal torque1, dReal torque2) +{ + dVector3 axis1, axis2; + dAASSERT(joint); + dUASSERT(joint->vtable == &__duniversal_vtable,"joint is not a universal"); + + if (joint->flags & dJOINT_REVERSE) { + dReal temp = torque1; + torque1 = - torque2; + torque2 = - temp; + } + + getAxis (joint,axis1,joint->axis1); + getAxis2 (joint,axis2,joint->axis2); + axis1[0] = axis1[0] * torque1 + axis2[0] * torque2; + axis1[1] = axis1[1] * torque1 + axis2[1] * torque2; + axis1[2] = axis1[2] * torque1 + axis2[2] * torque2; + + if (joint->node[0].body != 0) + dBodyAddTorque (joint->node[0].body,axis1[0],axis1[1],axis1[2]); + if (joint->node[1].body != 0) + dBodyAddTorque(joint->node[1].body, -axis1[0], -axis1[1], -axis1[2]); +} + + + + + +dxJoint::Vtable __duniversal_vtable = { + sizeof(dxJointUniversal), + (dxJoint::init_fn*) universalInit, + (dxJoint::getInfo1_fn*) universalGetInfo1, + (dxJoint::getInfo2_fn*) universalGetInfo2, + dJointTypeUniversal}; + +//**************************************************************************** +// angular motor + +static void amotorInit (dxJointAMotor *j) +{ + int i; + j->num = 0; + j->mode = dAMotorUser; + for (i=0; i<3; i++) { + j->rel[i] = 0; + dSetZero (j->axis[i],4); + j->limot[i].init (j->world); + j->angle[i] = 0; + } + dSetZero (j->reference1,4); + dSetZero (j->reference2,4); +} + + +// compute the 3 axes in global coordinates + +static void amotorComputeGlobalAxes (dxJointAMotor *joint, dVector3 ax[3]) +{ + if (joint->mode == dAMotorEuler) { + // special handling for euler mode + dMULTIPLY0_331 (ax[0],joint->node[0].body->R,joint->axis[0]); + if (joint->node[1].body) { + dMULTIPLY0_331 (ax[2],joint->node[1].body->R,joint->axis[2]); + } + else { + ax[2][0] = joint->axis[2][0]; + ax[2][1] = joint->axis[2][1]; + ax[2][2] = joint->axis[2][2]; + } + dCROSS (ax[1],=,ax[2],ax[0]); + dNormalize3 (ax[1]); + } + else { + for (int i=0; i < joint->num; i++) { + if (joint->rel[i] == 1) { + // relative to b1 + dMULTIPLY0_331 (ax[i],joint->node[0].body->R,joint->axis[i]); + } + if (joint->rel[i] == 2) { + // relative to b2 + dIASSERT(joint->node[1].body); + dMULTIPLY0_331 (ax[i],joint->node[1].body->R,joint->axis[i]); + } + else { + // global - just copy it + ax[i][0] = joint->axis[i][0]; + ax[i][1] = joint->axis[i][1]; + ax[i][2] = joint->axis[i][2]; + } + } + } +} + + +static void amotorComputeEulerAngles (dxJointAMotor *joint, dVector3 ax[3]) +{ + // assumptions: + // global axes already calculated --> ax + // axis[0] is relative to body 1 --> global ax[0] + // axis[2] is relative to body 2 --> global ax[2] + // ax[1] = ax[2] x ax[0] + // original ax[0] and ax[2] are perpendicular + // reference1 is perpendicular to ax[0] (in body 1 frame) + // reference2 is perpendicular to ax[2] (in body 2 frame) + // all ax[] and reference vectors are unit length + + // calculate references in global frame + dVector3 ref1,ref2; + dMULTIPLY0_331 (ref1,joint->node[0].body->R,joint->reference1); + if (joint->node[1].body) { + dMULTIPLY0_331 (ref2,joint->node[1].body->R,joint->reference2); + } + else { + ref2[0] = joint->reference2[0]; + ref2[1] = joint->reference2[1]; + ref2[2] = joint->reference2[2]; + } + + // get q perpendicular to both ax[0] and ref1, get first euler angle + dVector3 q; + dCROSS (q,=,ax[0],ref1); + joint->angle[0] = -dAtan2 (dDOT(ax[2],q),dDOT(ax[2],ref1)); + + // get q perpendicular to both ax[0] and ax[1], get second euler angle + dCROSS (q,=,ax[0],ax[1]); + joint->angle[1] = -dAtan2 (dDOT(ax[2],ax[0]),dDOT(ax[2],q)); + + // get q perpendicular to both ax[1] and ax[2], get third euler angle + dCROSS (q,=,ax[1],ax[2]); + joint->angle[2] = -dAtan2 (dDOT(ref2,ax[1]), dDOT(ref2,q)); +} + + +// set the reference vectors as follows: +// * reference1 = current axis[2] relative to body 1 +// * reference2 = current axis[0] relative to body 2 +// this assumes that: +// * axis[0] is relative to body 1 +// * axis[2] is relative to body 2 + +static void amotorSetEulerReferenceVectors (dxJointAMotor *j) +{ + if (j->node[0].body && j->node[1].body) { + dVector3 r; // axis[2] and axis[0] in global coordinates + dMULTIPLY0_331 (r,j->node[1].body->R,j->axis[2]); + dMULTIPLY1_331 (j->reference1,j->node[0].body->R,r); + dMULTIPLY0_331 (r,j->node[0].body->R,j->axis[0]); + dMULTIPLY1_331 (j->reference2,j->node[1].body->R,r); + } + else if (j->node[0].body) { + dMULTIPLY1_331 (j->reference1,j->node[0].body->R,j->axis[2]); + dMULTIPLY0_331 (j->reference2,j->node[0].body->R,j->axis[0]); + } +} + + +static void amotorGetInfo1 (dxJointAMotor *j, dxJoint::Info1 *info) +{ + info->m = 0; + info->nub = 0; + + // compute the axes and angles, if in euler mode + if (j->mode == dAMotorEuler) { + dVector3 ax[3]; + amotorComputeGlobalAxes (j,ax); + amotorComputeEulerAngles (j,ax); + } + + // see if we're powered or at a joint limit for each axis + for (int i=0; i < j->num; i++) { + if (j->limot[i].testRotationalLimit (j->angle[i]) || + j->limot[i].fmax > 0) { + info->m++; + } + } +} + + +static void amotorGetInfo2 (dxJointAMotor *joint, dxJoint::Info2 *info) +{ + int i; + + // compute the axes (if not global) + dVector3 ax[3]; + amotorComputeGlobalAxes (joint,ax); + + // in euler angle mode we do not actually constrain the angular velocity + // along the axes axis[0] and axis[2] (although we do use axis[1]) : + // + // to get constrain w2-w1 along ...not + // ------ --------------------- ------ + // d(angle[0])/dt = 0 ax[1] x ax[2] ax[0] + // d(angle[1])/dt = 0 ax[1] + // d(angle[2])/dt = 0 ax[0] x ax[1] ax[2] + // + // constraining w2-w1 along an axis 'a' means that a'*(w2-w1)=0. + // to prove the result for angle[0], write the expression for angle[0] from + // GetInfo1 then take the derivative. to prove this for angle[2] it is + // easier to take the euler rate expression for d(angle[2])/dt with respect + // to the components of w and set that to 0. + + dVector3 *axptr[3]; + axptr[0] = &ax[0]; + axptr[1] = &ax[1]; + axptr[2] = &ax[2]; + + dVector3 ax0_cross_ax1; + dVector3 ax1_cross_ax2; + if (joint->mode == dAMotorEuler) { + dCROSS (ax0_cross_ax1,=,ax[0],ax[1]); + axptr[2] = &ax0_cross_ax1; + dCROSS (ax1_cross_ax2,=,ax[1],ax[2]); + axptr[0] = &ax1_cross_ax2; + } + + int row=0; + for (i=0; i < joint->num; i++) { + row += joint->limot[i].addLimot (joint,info,row,*(axptr[i]),1); + } +} + + +extern "C" void dJointSetAMotorNumAxes (dxJointAMotor *joint, int num) +{ + dAASSERT(joint && num >= 0 && num <= 3); + dUASSERT(joint->vtable == &__damotor_vtable,"joint is not an amotor"); + if (joint->mode == dAMotorEuler) { + joint->num = 3; + } + else { + if (num < 0) num = 0; + if (num > 3) num = 3; + joint->num = num; + } +} + + +extern "C" void dJointSetAMotorAxis (dxJointAMotor *joint, int anum, int rel, + dReal x, dReal y, dReal z) +{ + dAASSERT(joint && anum >= 0 && anum <= 2 && rel >= 0 && rel <= 2); + dUASSERT(joint->vtable == &__damotor_vtable,"joint is not an amotor"); + dUASSERT(!(!joint->node[1].body && (joint->flags & dJOINT_REVERSE) && rel == 1),"no first body, can't set axis rel=1"); + dUASSERT(!(!joint->node[1].body && !(joint->flags & dJOINT_REVERSE) && rel == 2),"no second body, can't set axis rel=2"); + if (anum < 0) anum = 0; + if (anum > 2) anum = 2; + + // adjust rel to match the internal body order + if (!joint->node[1].body && rel==2) rel = 1; + + joint->rel[anum] = rel; + + // x,y,z is always in global coordinates regardless of rel, so we may have + // to convert it to be relative to a body + dVector3 r; + r[0] = x; + r[1] = y; + r[2] = z; + r[3] = 0; + if (rel > 0) { + if (rel==1) { + dMULTIPLY1_331 (joint->axis[anum],joint->node[0].body->R,r); + } + else { + dIASSERT (joint->node[1].body); + dMULTIPLY1_331 (joint->axis[anum],joint->node[1].body->R,r); + } + } + else { + joint->axis[anum][0] = r[0]; + joint->axis[anum][1] = r[1]; + joint->axis[anum][2] = r[2]; + } + dNormalize3 (joint->axis[anum]); + if (joint->mode == dAMotorEuler) amotorSetEulerReferenceVectors (joint); +} + + +extern "C" void dJointSetAMotorAngle (dxJointAMotor *joint, int anum, + dReal angle) +{ + dAASSERT(joint && anum >= 0 && anum < 3); + dUASSERT(joint->vtable == &__damotor_vtable,"joint is not an amotor"); + if (joint->mode == dAMotorUser) { + if (anum < 0) anum = 0; + if (anum > 3) anum = 3; + joint->angle[anum] = angle; + } +} + + +extern "C" void dJointSetErp (dJointID joint, dReal erp) // Philip +{ + dAASSERT(joint); + joint->erp = erp; +} + +extern "C" void dJointSetAMotorParam (dxJointAMotor *joint, int parameter, + dReal value) +{ + dAASSERT(joint); + dUASSERT(joint->vtable == &__damotor_vtable,"joint is not an amotor"); + int anum = parameter >> 8; + if (anum < 0) anum = 0; + if (anum > 2) anum = 2; + parameter &= 0xff; + joint->limot[anum].set (parameter, value); +} + + +extern "C" void dJointSetAMotorMode (dxJointAMotor *joint, int mode) +{ + dAASSERT(joint); + dUASSERT(joint->vtable == &__damotor_vtable,"joint is not an amotor"); + joint->mode = mode; + if (joint->mode == dAMotorEuler) { + joint->num = 3; + amotorSetEulerReferenceVectors (joint); + } +} + + +extern "C" int dJointGetAMotorNumAxes (dxJointAMotor *joint) +{ + dAASSERT(joint); + dUASSERT(joint->vtable == &__damotor_vtable,"joint is not an amotor"); + return joint->num; +} + + +extern "C" void dJointGetAMotorAxis (dxJointAMotor *joint, int anum, + dVector3 result) +{ + dAASSERT(joint && anum >= 0 && anum < 3); + dUASSERT(joint->vtable == &__damotor_vtable,"joint is not an amotor"); + if (anum < 0) anum = 0; + if (anum > 2) anum = 2; + if (joint->rel[anum] > 0) { + if (joint->rel[anum]==1) { + dMULTIPLY0_331 (result,joint->node[0].body->R,joint->axis[anum]); + } + else { + dMULTIPLY0_331 (result,joint->node[1].body->R,joint->axis[anum]); + } + } + else { + result[0] = joint->axis[anum][0]; + result[1] = joint->axis[anum][1]; + result[2] = joint->axis[anum][2]; + } +} + + +extern "C" int dJointGetAMotorAxisRel (dxJointAMotor *joint, int anum) +{ + dAASSERT(joint && anum >= 0 && anum < 3); + dUASSERT(joint->vtable == &__damotor_vtable,"joint is not an amotor"); + if (anum < 0) anum = 0; + if (anum > 2) anum = 2; + return joint->rel[anum]; +} + + +extern "C" dReal dJointGetAMotorAngle (dxJointAMotor *joint, int anum) +{ + dAASSERT(joint && anum >= 0 && anum < 3); + dUASSERT(joint->vtable == &__damotor_vtable,"joint is not an amotor"); + if (anum < 0) anum = 0; + if (anum > 3) anum = 3; + return joint->angle[anum]; +} + + +extern "C" dReal dJointGetAMotorAngleRate (dxJointAMotor *joint, int anum) +{ + // @@@ + dDebug (0,"not yet implemented"); + return 0; +} + + +extern "C" dReal dJointGetAMotorParam (dxJointAMotor *joint, int parameter) +{ + dAASSERT(joint); + dUASSERT(joint->vtable == &__damotor_vtable,"joint is not an amotor"); + int anum = parameter >> 8; + if (anum < 0) anum = 0; + if (anum > 2) anum = 2; + parameter &= 0xff; + return joint->limot[anum].get (parameter); +} + + +extern "C" int dJointGetAMotorMode (dxJointAMotor *joint) +{ + dAASSERT(joint); + dUASSERT(joint->vtable == &__damotor_vtable,"joint is not an amotor"); + return joint->mode; +} + + +extern "C" void dJointAddAMotorTorques (dxJointAMotor *joint, dReal torque1, dReal torque2, dReal torque3) +{ + dVector3 axes[3]; + dAASSERT(joint); + dUASSERT(joint->vtable == &__damotor_vtable,"joint is not an amotor"); + + if (joint->num == 0) + return; + dUASSERT((joint->flags & dJOINT_REVERSE) == 0, "dJointAddAMotorTorques not yet implemented for reverse AMotor joints"); + + amotorComputeGlobalAxes (joint,axes); + axes[0][0] *= torque1; + axes[0][1] *= torque1; + axes[0][2] *= torque1; + if (joint->num >= 2) { + axes[0][0] += axes[1][0] * torque2; + axes[0][1] += axes[1][0] * torque2; + axes[0][2] += axes[1][0] * torque2; + if (joint->num >= 3) { + axes[0][0] += axes[2][0] * torque3; + axes[0][1] += axes[2][0] * torque3; + axes[0][2] += axes[2][0] * torque3; + } + } + + if (joint->node[0].body != 0) + dBodyAddTorque (joint->node[0].body,axes[0][0],axes[0][1],axes[0][2]); + if (joint->node[1].body != 0) + dBodyAddTorque(joint->node[1].body, -axes[0][0], -axes[0][1], -axes[0][2]); +} + + +dxJoint::Vtable __damotor_vtable = { + sizeof(dxJointAMotor), + (dxJoint::init_fn*) amotorInit, + (dxJoint::getInfo1_fn*) amotorGetInfo1, + (dxJoint::getInfo2_fn*) amotorGetInfo2, + dJointTypeAMotor}; + +//**************************************************************************** +// fixed joint + +static void fixedInit (dxJointFixed *j) +{ + dSetZero (j->offset,4); + dSetZero (j->qrel,4); +} + + +static void fixedGetInfo1 (dxJointFixed *j, dxJoint::Info1 *info) +{ + info->m = 6; + info->nub = 6; +} + + +static void fixedGetInfo2 (dxJointFixed *joint, dxJoint::Info2 *info) +{ + int s = info->rowskip; + + // Three rows for orientation + setFixedOrientation(joint, info, joint->qrel, 3); + + // Three rows for position. + // set jacobian + info->J1l[0] = 1; + info->J1l[s+1] = 1; + info->J1l[2*s+2] = 1; + + dVector3 ofs; + dMULTIPLY0_331 (ofs,joint->node[0].body->R,joint->offset); + if (joint->node[1].body) { + dCROSSMAT (info->J1a,ofs,s,+,-); + info->J2l[0] = -1; + info->J2l[s+1] = -1; + info->J2l[2*s+2] = -1; + } + + // set right hand side for the first three rows (linear) + dReal k = info->fps * info->erp; + if (joint->node[1].body) { + for (int j=0; j<3; j++) + info->c[j] = k * (joint->node[1].body->pos[j] - + joint->node[0].body->pos[j] + ofs[j]); + } + else { + for (int j=0; j<3; j++) + info->c[j] = k * (joint->offset[j] - joint->node[0].body->pos[j]); + } +} + + +extern "C" void dJointSetFixed (dxJointFixed *joint) +{ + dUASSERT(joint,"bad joint argument"); + dUASSERT(joint->vtable == &__dfixed_vtable,"joint is not fixed"); + int i; + + // This code is taken from sJointSetSliderAxis(), we should really put the + // common code in its own function. + // compute the offset between the bodies + if (joint->node[0].body) { + if (joint->node[1].body) { + dQMultiply1 (joint->qrel,joint->node[0].body->q,joint->node[1].body->q); + dReal ofs[4]; + for (i=0; i<4; i++) ofs[i] = joint->node[0].body->pos[i]; + for (i=0; i<4; i++) ofs[i] -= joint->node[1].body->pos[i]; + dMULTIPLY1_331 (joint->offset,joint->node[0].body->R,ofs); + } + else { + // set joint->qrel to the transpose of the first body's q + joint->qrel[0] = joint->node[0].body->q[0]; + for (i=1; i<4; i++) joint->qrel[i] = -joint->node[0].body->q[i]; + for (i=0; i<4; i++) joint->offset[i] = joint->node[0].body->pos[i]; + } + } +} + + +dxJoint::Vtable __dfixed_vtable = { + sizeof(dxJointFixed), + (dxJoint::init_fn*) fixedInit, + (dxJoint::getInfo1_fn*) fixedGetInfo1, + (dxJoint::getInfo2_fn*) fixedGetInfo2, + dJointTypeFixed}; + +//**************************************************************************** +// null joint + +static void nullGetInfo1 (dxJointNull *j, dxJoint::Info1 *info) +{ + info->m = 0; + info->nub = 0; +} + + +static void nullGetInfo2 (dxJointNull *joint, dxJoint::Info2 *info) +{ + dDebug (0,"this should never get called"); +} + + +dxJoint::Vtable __dnull_vtable = { + sizeof(dxJointNull), + (dxJoint::init_fn*) 0, + (dxJoint::getInfo1_fn*) nullGetInfo1, + (dxJoint::getInfo2_fn*) nullGetInfo2, + dJointTypeNull}; diff --git a/HenocUniverse/ode/source/joint.h b/HenocUniverse/ode/source/joint.h new file mode 100755 index 0000000..1a93c8d --- /dev/null +++ b/HenocUniverse/ode/source/joint.h @@ -0,0 +1,268 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef _ODE_JOINT_H_ +#define _ODE_JOINT_H_ + + +#include "objects.h" +#include +#include "obstack.h" + + +// joint flags +enum { + // if this flag is set, the joint was allocated in a joint group + dJOINT_INGROUP = 1, + + // if this flag is set, the joint was attached with arguments (0,body). + // our convention is to treat all attaches as (body,0), i.e. so node[0].body + // is always nonzero, so this flag records the fact that the arguments were + // swapped. + dJOINT_REVERSE = 2, + + // if this flag is set, the joint can not have just one body attached to it, + // it must have either zero or two bodies attached. + dJOINT_TWOBODIES = 4 +}; + + +// there are two of these nodes in the joint, one for each connection to a +// body. these are node of a linked list kept by each body of it's connecting +// joints. but note that the body pointer in each node points to the body that +// makes use of the *other* node, not this node. this trick makes it a bit +// easier to traverse the body/joint graph. + +struct dxJointNode { + dxJoint *joint; // pointer to enclosing dxJoint object + dxBody *body; // *other* body this joint is connected to + dxJointNode *next; // next node in body's list of connected joints +}; + + +struct dxJoint : public dObject { + // naming convention: the "first" body this is connected to is node[0].body, + // and the "second" body is node[1].body. if this joint is only connected + // to one body then the second body is 0. + + // info returned by getInfo1 function. the constraint dimension is m (<=6). + // i.e. that is the total number of rows in the jacobian. `nub' is the + // number of unbounded variables (which have lo,hi = -/+ infinity). + + struct Info1 { + int m,nub; + }; + + // info returned by getInfo2 function + + struct Info2 { + // integrator parameters: frames per second (1/stepsize), default error + // reduction parameter (0..1). + dReal fps,erp; + + // for the first and second body, pointers to two (linear and angular) + // n*3 jacobian sub matrices, stored by rows. these matrices will have + // been initialized to 0 on entry. if the second body is zero then the + // J2xx pointers may be 0. + dReal *J1l,*J1a,*J2l,*J2a; + + // elements to jump from one row to the next in J's + int rowskip; + + // right hand sides of the equation J*v = c + cfm * lambda. cfm is the + // "constraint force mixing" vector. c is set to zero on entry, cfm is + // set to a constant value (typically very small or zero) value on entry. + dReal *c,*cfm; + + // lo and hi limits for variables (set to -/+ infinity on entry). + dReal *lo,*hi; + + // findex vector for variables. see the LCP solver interface for a + // description of what this does. this is set to -1 on entry. + // note that the returned indexes are relative to the first index of + // the constraint. + int *findex; + }; + + // virtual function table: size of the joint structure, function pointers. + // we do it this way instead of using C++ virtual functions because + // sometimes we need to allocate joints ourself within a memory pool. + + typedef void init_fn (dxJoint *joint); + typedef void getInfo1_fn (dxJoint *joint, Info1 *info); + typedef void getInfo2_fn (dxJoint *joint, Info2 *info); + struct Vtable { + int size; + init_fn *init; + getInfo1_fn *getInfo1; + getInfo2_fn *getInfo2; + int typenum; // a dJointTypeXXX type number + }; + + Vtable *vtable; // virtual function table + int flags; // dJOINT_xxx flags + dxJointNode node[2]; // connections to bodies. node[1].body can be 0 + dJointFeedback *feedback; // optional feedback structure + dReal lambda[6]; // lambda generated by last step + dReal erp; // Philip +}; + + +// joint group. NOTE: any joints in the group that have their world destroyed +// will have their world pointer set to 0. + +struct dxJointGroup : public dBase { + int num; // number of joints on the stack + dObStack stack; // a stack of (possibly differently sized) dxJoint +}; // objects. + + +// common limit and motor information for a single joint axis of movement +struct dxJointLimitMotor { + dReal vel,fmax; // powered joint: velocity, max force + dReal lostop,histop; // joint limits, relative to initial position + dReal fudge_factor; // when powering away from joint limits + dReal normal_cfm; // cfm to use when not at a stop + dReal stop_erp,stop_cfm; // erp and cfm for when at joint limit + dReal bounce; // restitution factor + // variables used between getInfo1() and getInfo2() + int limit; // 0=free, 1=at lo limit, 2=at hi limit + dReal limit_err; // if at limit, amount over limit + + void init (dxWorld *); + void set (int num, dReal value); + dReal get (int num); + int testRotationalLimit (dReal angle); + int addLimot (dxJoint *joint, dxJoint::Info2 *info, int row, + dVector3 ax1, int rotational); +}; + + +// ball and socket + +struct dxJointBall : public dxJoint { + dVector3 anchor1; // anchor w.r.t first body + dVector3 anchor2; // anchor w.r.t second body +}; +extern struct dxJoint::Vtable __dball_vtable; + + +// hinge + +struct dxJointHinge : public dxJoint { + dVector3 anchor1; // anchor w.r.t first body + dVector3 anchor2; // anchor w.r.t second body + dVector3 axis1; // axis w.r.t first body + dVector3 axis2; // axis w.r.t second body + dQuaternion qrel; // initial relative rotation body1 -> body2 + dxJointLimitMotor limot; // limit and motor information +}; +extern struct dxJoint::Vtable __dhinge_vtable; + + +// universal + +struct dxJointUniversal : public dxJoint { + dVector3 anchor1; // anchor w.r.t first body + dVector3 anchor2; // anchor w.r.t second body + dVector3 axis1; // axis w.r.t first body + dVector3 axis2; // axis w.r.t second body + dQuaternion qrel1; // initial relative rotation body1 -> virtual cross piece + dQuaternion qrel2; // initial relative rotation virtual cross piece -> body2 + dxJointLimitMotor limot1; // limit and motor information for axis1 + dxJointLimitMotor limot2; // limit and motor information for axis2 +}; +extern struct dxJoint::Vtable __duniversal_vtable; + + +// slider. if body2 is 0 then qrel is the absolute rotation of body1 and +// offset is the position of body1 center along axis1. + +struct dxJointSlider : public dxJoint { + dVector3 axis1; // axis w.r.t first body + dQuaternion qrel; // initial relative rotation body1 -> body2 + dVector3 offset; // point relative to body2 that should be + // aligned with body1 center along axis1 + dxJointLimitMotor limot; // limit and motor information +}; +extern struct dxJoint::Vtable __dslider_vtable; + + +// contact + +struct dxJointContact : public dxJoint { + int the_m; // number of rows computed by getInfo1 + dContact contact; +}; +extern struct dxJoint::Vtable __dcontact_vtable; + + +// hinge 2 + +struct dxJointHinge2 : public dxJoint { + dVector3 anchor1; // anchor w.r.t first body + dVector3 anchor2; // anchor w.r.t second body + dVector3 axis1; // axis 1 w.r.t first body + dVector3 axis2; // axis 2 w.r.t second body + dReal c0,s0; // cos,sin of desired angle between axis 1,2 + dVector3 v1,v2; // angle ref vectors embedded in first body + dxJointLimitMotor limot1; // limit+motor info for axis 1 + dxJointLimitMotor limot2; // limit+motor info for axis 2 + dReal susp_erp,susp_cfm; // suspension parameters (erp,cfm) +}; +extern struct dxJoint::Vtable __dhinge2_vtable; + + +// angular motor + +struct dxJointAMotor : public dxJoint { + int num; // number of axes (0..3) + int mode; // a dAMotorXXX constant + int rel[3]; // what the axes are relative to (global,b1,b2) + dVector3 axis[3]; // three axes + dxJointLimitMotor limot[3]; // limit+motor info for axes + dReal angle[3]; // user-supplied angles for axes + // these vectors are used for calculating euler angles + dVector3 reference1; // original axis[2], relative to body 1 + dVector3 reference2; // original axis[0], relative to body 2 +}; +extern struct dxJoint::Vtable __damotor_vtable; + + +// fixed + +struct dxJointFixed : public dxJoint { + dQuaternion qrel; // initial relative rotation body1 -> body2 + dVector3 offset; // relative offset between the bodies +}; +extern struct dxJoint::Vtable __dfixed_vtable; + + +// null joint, for testing only + +struct dxJointNull : public dxJoint { +}; +extern struct dxJoint::Vtable __dnull_vtable; + + + +#endif diff --git a/HenocUniverse/ode/source/lcp.cpp b/HenocUniverse/ode/source/lcp.cpp new file mode 100755 index 0000000..6389e93 --- /dev/null +++ b/HenocUniverse/ode/source/lcp.cpp @@ -0,0 +1,1474 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* + + +THE ALGORITHM +------------- + +solve A*x = b+w, with x and w subject to certain LCP conditions. +each x(i),w(i) must lie on one of the three line segments in the following +diagram. each line segment corresponds to one index set : + + w(i) + /|\ | : + | | : + | |i in N : + w>0 | |state[i]=0 : + | | : + | | : i in C + w=0 + +-----------------------+ + | : | + | : | + w<0 | : |i in N + | : |state[i]=1 + | : | + | : | + +-------|-----------|-----------|----------> x(i) + lo 0 hi + +the Dantzig algorithm proceeds as follows: + for i=1:n + * if (x(i),w(i)) is not on the line, push x(i) and w(i) positive or + negative towards the line. as this is done, the other (x(j),w(j)) + for j= 0. this makes the algorithm a bit +simpler, because the starting point for x(i),w(i) is always on the dotted +line x=0 and x will only ever increase in one direction, so it can only hit +two out of the three line segments. + + +NOTES +----- + +this is an implementation of "lcp_dantzig2_ldlt.m" and "lcp_dantzig_lohi.m". +the implementation is split into an LCP problem object (dLCP) and an LCP +driver function. most optimization occurs in the dLCP object. + +a naive implementation of the algorithm requires either a lot of data motion +or a lot of permutation-array lookup, because we are constantly re-ordering +rows and columns. to avoid this and make a more optimized algorithm, a +non-trivial data structure is used to represent the matrix A (this is +implemented in the fast version of the dLCP object). + +during execution of this algorithm, some indexes in A are clamped (set C), +some are non-clamped (set N), and some are "don't care" (where x=0). +A,x,b,w (and other problem vectors) are permuted such that the clamped +indexes are first, the unclamped indexes are next, and the don't-care +indexes are last. this permutation is recorded in the array `p'. +initially p = 0..n-1, and as the rows and columns of A,x,b,w are swapped, +the corresponding elements of p are swapped. + +because the C and N elements are grouped together in the rows of A, we can do +lots of work with a fast dot product function. if A,x,etc were not permuted +and we only had a permutation array, then those dot products would be much +slower as we would have a permutation array lookup in some inner loops. + +A is accessed through an array of row pointers, so that element (i,j) of the +permuted matrix is A[i][j]. this makes row swapping fast. for column swapping +we still have to actually move the data. + +during execution of this algorithm we maintain an L*D*L' factorization of +the clamped submatrix of A (call it `AC') which is the top left nC*nC +submatrix of A. there are two ways we could arrange the rows/columns in AC. + +(1) AC is always permuted such that L*D*L' = AC. this causes a problem + when a row/column is removed from C, because then all the rows/columns of A + between the deleted index and the end of C need to be rotated downward. + this results in a lot of data motion and slows things down. +(2) L*D*L' is actually a factorization of a *permutation* of AC (which is + itself a permutation of the underlying A). this is what we do - the + permutation is recorded in the vector C. call this permutation A[C,C]. + when a row/column is removed from C, all we have to do is swap two + rows/columns and manipulate C. + +*/ + +#include +#include "lcp.h" +#include +#include +#include "mat.h" // for testing +#include // for testing +#include +#include + +//*************************************************************************** +// code generation parameters + +// LCP debugging (mosty for fast dLCP) - this slows things down a lot +//#define DEBUG_LCP + +//#define dLCP_SLOW // use slow dLCP object +#define dLCP_FAST // use fast dLCP object + +// option 1 : matrix row pointers (less data copying) +#define ROWPTRS +#define ATYPE dReal ** +#define AROW(i) (A[i]) + +// option 2 : no matrix row pointers (slightly faster inner loops) +//#define NOROWPTRS +//#define ATYPE dReal * +//#define AROW(i) (A+(i)*nskip) + +// misc defines +#define ALLOCA dALLOCA16 +//#define dDot myDot +#define NUB_OPTIMIZATIONS + +//*************************************************************************** + +// an alternative inline dot product, for speed comparisons + +static inline dReal myDot (dReal *a, dReal *b, int n) +{ + dReal sum=0; + while (n > 0) { + sum += (*a) * (*b); + a++; + b++; + n--; + } + return sum; +} + + +// swap row/column i1 with i2 in the n*n matrix A. the leading dimension of +// A is nskip. this only references and swaps the lower triangle. +// if `do_fast_row_swaps' is nonzero and row pointers are being used, then +// rows will be swapped by exchanging row pointers. otherwise the data will +// be copied. + +static void swapRowsAndCols (ATYPE A, int n, int i1, int i2, int nskip, + int do_fast_row_swaps) +{ + int i; + dIASSERT (A && n > 0 && i1 >= 0 && i2 >= 0 && i1 < n && i2 < n && + nskip >= n && i1 < i2); + +# ifdef ROWPTRS + for (i=i1+1; i 0) { + memcpy (tmprow,A+i1*nskip,i1*sizeof(dReal)); + memcpy (A+i1*nskip,A+i2*nskip,i1*sizeof(dReal)); + memcpy (A+i2*nskip,tmprow,i1*sizeof(dReal)); + } + for (i=i1+1; i0 && i1 >=0 && i2 >= 0 && i1 < n && i2 < n && nskip >= n && + i1 <= i2); + if (i1==i2) return; + swapRowsAndCols (A,n,i1,i2,nskip,do_fast_row_swaps); + tmp = x[i1]; + x[i1] = x[i2]; + x[i2] = tmp; + tmp = b[i1]; + b[i1] = b[i2]; + b[i2] = tmp; + tmp = w[i1]; + w[i1] = w[i2]; + w[i2] = tmp; + tmp = lo[i1]; + lo[i1] = lo[i2]; + lo[i2] = tmp; + tmp = hi[i1]; + hi[i1] = hi[i2]; + hi[i2] = tmp; + tmpi = p[i1]; + p[i1] = p[i2]; + p[i2] = tmpi; + tmpi = state[i1]; + state[i1] = state[i2]; + state[i2] = tmpi; + if (findex) { + tmpi = findex[i1]; + findex[i1] = findex[i2]; + findex[i2] = tmpi; + } +} + + +// for debugging - check that L,d is the factorization of A[C,C]. +// A[C,C] has size nC*nC and leading dimension nskip. +// L has size nC*nC and leading dimension nskip. +// d has size nC. + +#ifdef DEBUG_LCP + +static void checkFactorization (ATYPE A, dReal *_L, dReal *_d, + int nC, int *C, int nskip) +{ + int i,j; + if (nC==0) return; + + // get A1=A, copy the lower triangle to the upper triangle, get A2=A[C,C] + dMatrix A1 (nC,nC); + for (i=0; i 1e-8) + dDebug (0,"L*D*L' check, maximum difference = %.6e\n",diff); +} + +#endif + + +// for debugging + +#ifdef DEBUG_LCP + +static void checkPermutations (int i, int n, int nC, int nN, int *p, int *C) +{ + int j,k; + dIASSERT (nC>=0 && nN>=0 && (nC+nN)==i && i < n); + for (k=0; k= 0 && p[k] < i); + for (k=i; k C,N; // index sets + int last_i_for_solve1; // last i value given to solve1 + + dLCP (int _n, int _nub, dReal *_Adata, dReal *_x, dReal *_b, dReal *_w, + dReal *_lo, dReal *_hi, dReal *_L, dReal *_d, + dReal *_Dell, dReal *_ell, dReal *_tmp, + int *_state, int *_findex, int *_p, int *_C, dReal **Arows); + // the constructor is given an initial problem description (A,x,b,w) and + // space for other working data (which the caller may allocate on the stack). + // some of this data is specific to the fast dLCP implementation. + // the matrices A and L have size n*n, vectors have size n*1. + // A represents a symmetric matrix but only the lower triangle is valid. + // `nub' is the number of unbounded indexes at the start. all the indexes + // 0..nub-1 will be put into C. + + ~dLCP(); + + int getNub() { return nub; } + // return the value of `nub'. the constructor may want to change it, + // so the caller should find out its new value. + + // transfer functions: transfer index i to the given set (C or N). indexes + // less than `nub' can never be given. A,x,b,w,etc may be permuted by these + // functions, the caller must be robust to this. + + void transfer_i_to_C (int i); + // this assumes C and N span 1:i-1. this also assumes that solve1() has + // been recently called for the same i without any other transfer + // functions in between (thereby allowing some data reuse for the fast + // implementation). + void transfer_i_to_N (int i); + // this assumes C and N span 1:i-1. + void transfer_i_from_N_to_C (int i); + void transfer_i_from_C_to_N (int i); + + int numC(); + int numN(); + // return the number of indexes in set C/N + + int indexC (int i); + int indexN (int i); + // return index i in set C/N. + + // accessor and arithmetic functions. Aij translates as A(i,j), etc. + // make sure that only the lower triangle of A is ever referenced. + + dReal Aii (int i); + dReal AiC_times_qC (int i, dReal *q); + dReal AiN_times_qN (int i, dReal *q); // for all Nj + void pN_equals_ANC_times_qC (dReal *p, dReal *q); // for all Nj + void pN_plusequals_ANi (dReal *p, int i, int sign=1); + // for all Nj. sign = +1,-1. assumes i > maximum index in N. + void pC_plusequals_s_times_qC (dReal *p, dReal s, dReal *q); + void pN_plusequals_s_times_qN (dReal *p, dReal s, dReal *q); // for all Nj + void solve1 (dReal *a, int i, int dir=1, int only_transfer=0); + // get a(C) = - dir * A(C,C) \ A(C,i). dir must be +/- 1. + // the fast version of this function computes some data that is needed by + // transfer_i_to_C(). if only_transfer is nonzero then this function + // *only* computes that data, it does not set a(C). + + void unpermute(); + // call this at the end of the LCP function. if the x/w values have been + // permuted then this will unscramble them. +}; + + +dLCP::dLCP (int _n, int _nub, dReal *_Adata, dReal *_x, dReal *_b, dReal *_w, + dReal *_lo, dReal *_hi, dReal *_L, dReal *_d, + dReal *_Dell, dReal *_ell, dReal *_tmp, + int *_state, int *_findex, int *_p, int *_C, dReal **Arows) +{ + dUASSERT (_findex==0,"slow dLCP object does not support findex array"); + + n = _n; + nub = _nub; + Adata = _Adata; + A = 0; + x = _x; + b = _b; + w = _w; + lo = _lo; + hi = _hi; + nskip = dPAD(n); + dSetZero (x,n); + last_i_for_solve1 = -1; + + int i,j; + C.setSize (n); + N.setSize (n); + for (int i=0; i0, put all indexes 0..nub-1 into C and solve for x + if (nub > 0) { + for (i=0; i= i) dDebug (0,"N assumption violated"); + if (sign > 0) { + for (k=0; k 0) { + for (ii=0; ii nub + if (nub < n) { + for (k=0; k<100; k++) { + int i1,i2; + do { + i1 = dRandInt(n-nub)+nub; + i2 = dRandInt(n-nub)+nub; + } + while (i1 > i2); + //printf ("--> %d %d\n",i1,i2); + swapProblem (A,x,b,w,lo,hi,p,state,findex,n,i1,i2,nskip,0); + } + } + */ + + // permute the problem so that *all* the unbounded variables are at the + // start, i.e. look for unbounded variables not included in `nub'. we can + // potentially push up `nub' this way and get a bigger initial factorization. + // note that when we swap rows/cols here we must not just swap row pointers, + // as the initial factorization relies on the data being all in one chunk. + // variables that have findex >= 0 are *not* considered to be unbounded even + // if lo=-inf and hi=inf - this is because these limits may change during the + // solution process. + + for (k=nub; k= 0) continue; + if (lo[k]==-dInfinity && hi[k]==dInfinity) { + swapProblem (A,x,b,w,lo,hi,p,state,findex,n,nub,k,nskip,0); + nub++; + } + } + + // if there are unbounded variables at the start, factorize A up to that + // point and solve for x. this puts all indexes 0..nub-1 into C. + if (nub > 0) { + for (k=0; k nub such that all findex variables are at the end + if (findex) { + int num_at_end = 0; + for (k=n-1; k >= nub; k--) { + if (findex[k] >= 0) { + swapProblem (A,x,b,w,lo,hi,p,state,findex,n,k,n-1-num_at_end,nskip,1); + num_at_end++; + } + } + } + + // print info about indexes + /* + for (k=0; k 0) { + // ell,Dell were computed by solve1(). note, ell = D \ L1solve (L,A(i,C)) + for (j=0; j 0) { + dReal *aptr = AROW(i); +# ifdef NUB_OPTIMIZATIONS + // if nub>0, initial part of aptr unpermuted + for (j=0; j 0) { + for (int i=0; i 0) { + dReal *aptr = AROW(i); +# ifdef NUB_OPTIMIZATIONS + // if nub>0, initial part of aptr[] is guaranteed unpermuted + for (j=0; j 0) { + for (j=0; j0 && A && x && b && w && nub == 0); + + int i,k; + int nskip = dPAD(n); + dReal *L = (dReal*) ALLOCA (n*nskip*sizeof(dReal)); + dReal *d = (dReal*) ALLOCA (n*sizeof(dReal)); + dReal *delta_x = (dReal*) ALLOCA (n*sizeof(dReal)); + dReal *delta_w = (dReal*) ALLOCA (n*sizeof(dReal)); + dReal *Dell = (dReal*) ALLOCA (n*sizeof(dReal)); + dReal *ell = (dReal*) ALLOCA (n*sizeof(dReal)); + dReal *tmp = (dReal*) ALLOCA (n*sizeof(dReal)); + dReal **Arows = (dReal**) ALLOCA (n*sizeof(dReal*)); + int *p = (int*) ALLOCA (n*sizeof(int)); + int *C = (int*) ALLOCA (n*sizeof(int)); + int *dummy = (int*) ALLOCA (n*sizeof(int)); + + dLCP lcp (n,0,A,x,b,w,tmp,tmp,L,d,Dell,ell,tmp,dummy,dummy,p,C,Arows); + nub = lcp.getNub(); + + for (i=0; i= 0) { + lcp.transfer_i_to_N (i); + } + else { + for (;;) { + // compute: delta_x(C) = -A(C,C)\A(C,i) + dSetZero (delta_x,n); + lcp.solve1 (delta_x,i); + delta_x[i] = 1; + + // compute: delta_w = A*delta_x + dSetZero (delta_w,n); + lcp.pN_equals_ANC_times_qC (delta_w,delta_x); + lcp.pN_plusequals_ANi (delta_w,i); + delta_w[i] = lcp.AiC_times_qC (i,delta_x) + lcp.Aii(i); + + // find index to switch + int si = i; // si = switch index + int si_in_N = 0; // set to 1 if si in N + dReal s = -w[i]/delta_w[i]; + + if (s <= 0) { + dMessage (d_ERR_LCP, "LCP internal error, s <= 0 (s=%.4e)",s); + if (i < (n-1)) { + dSetZero (x+i,n-i); + dSetZero (w+i,n-i); + } + goto done; + } + + for (k=0; k < lcp.numN(); k++) { + if (delta_w[lcp.indexN(k)] < 0) { + dReal s2 = -w[lcp.indexN(k)] / delta_w[lcp.indexN(k)]; + if (s2 < s) { + s = s2; + si = lcp.indexN(k); + si_in_N = 1; + } + } + } + for (k=0; k < lcp.numC(); k++) { + if (delta_x[lcp.indexC(k)] < 0) { + dReal s2 = -x[lcp.indexC(k)] / delta_x[lcp.indexC(k)]; + if (s2 < s) { + s = s2; + si = lcp.indexC(k); + si_in_N = 0; + } + } + } + + // apply x = x + s * delta_x + lcp.pC_plusequals_s_times_qC (x,s,delta_x); + x[i] += s; + lcp.pN_plusequals_s_times_qN (w,s,delta_w); + w[i] += s * delta_w[i]; + + // switch indexes between sets if necessary + if (si==i) { + w[i] = 0; + lcp.transfer_i_to_C (i); + break; + } + if (si_in_N) { + w[si] = 0; + lcp.transfer_i_from_N_to_C (si); + } + else { + x[si] = 0; + lcp.transfer_i_from_C_to_N (si); + } + } + } + } + + done: + lcp.unpermute(); +} + +//*************************************************************************** +// an optimized Dantzig LCP driver routine for the lo-hi LCP problem. + +void dSolveLCP (int n, dReal *A, dReal *x, dReal *b, + dReal *w, int nub, dReal *lo, dReal *hi, int *findex) +{ + dAASSERT (n>0 && A && x && b && w && lo && hi && nub >= 0 && nub <= n); + int i,k,hit_first_friction_index = 0; + int nskip = dPAD(n); + + // if all the variables are unbounded then we can just factor, solve, + // and return + if (nub >= n) { + dFactorLDLT (A,w,n,nskip); // use w for d + dSolveLDLT (A,w,b,n,nskip); + memcpy (x,b,n*sizeof(dReal)); + dSetZero (w,n); + return; + } + +# ifndef dNODEBUG + // check restrictions on lo and hi + for (k=0; k= 0); +# endif + + dReal *L = (dReal*) ALLOCA (n*nskip*sizeof(dReal)); + dReal *d = (dReal*) ALLOCA (n*sizeof(dReal)); + dReal *delta_x = (dReal*) ALLOCA (n*sizeof(dReal)); + dReal *delta_w = (dReal*) ALLOCA (n*sizeof(dReal)); + dReal *Dell = (dReal*) ALLOCA (n*sizeof(dReal)); + dReal *ell = (dReal*) ALLOCA (n*sizeof(dReal)); + dReal **Arows = (dReal**) ALLOCA (n*sizeof(dReal*)); + int *p = (int*) ALLOCA (n*sizeof(int)); + int *C = (int*) ALLOCA (n*sizeof(int)); + int dir; + dReal dirf; + + // for i in N, state[i] is 0 if x(i)==lo(i) or 1 if x(i)==hi(i) + int *state = (int*) ALLOCA (n*sizeof(int)); + + // create LCP object. note that tmp is set to delta_w to save space, this + // optimization relies on knowledge of how tmp is used, so be careful! + dLCP lcp (n,nub,A,x,b,w,lo,hi,L,d,Dell,ell,delta_w,state,findex,p,C,Arows); + nub = lcp.getNub(); + + // loop over all indexes nub..n-1. for index i, if x(i),w(i) satisfy the + // LCP conditions then i is added to the appropriate index set. otherwise + // x(i),w(i) is driven either +ve or -ve to force it to the valid region. + // as we drive x(i), x(C) is also adjusted to keep w(C) at zero. + // while driving x(i) we maintain the LCP conditions on the other variables + // 0..i-1. we do this by watching out for other x(i),w(i) values going + // outside the valid region, and then switching them between index sets + // when that happens. + + for (i=nub; i= 0) { + // un-permute x into delta_w, which is not being used at the moment + for (k=0; k= 0) { + lcp.transfer_i_to_N (i); + state[i] = 0; + } + else if (hi[i]==0 && w[i] <= 0) { + lcp.transfer_i_to_N (i); + state[i] = 1; + } + else if (w[i]==0) { + // this is a degenerate case. by the time we get to this test we know + // that lo != 0, which means that lo < 0 as lo is not allowed to be +ve, + // and similarly that hi > 0. this means that the line segment + // corresponding to set C is at least finite in extent, and we are on it. + // NOTE: we must call lcp.solve1() before lcp.transfer_i_to_C() + lcp.solve1 (delta_x,i,0,1); + lcp.transfer_i_to_C (i); + } + else { + // we must push x(i) and w(i) + for (;;) { + // find direction to push on x(i) + if (w[i] <= 0) { + dir = 1; + dirf = REAL(1.0); + } + else { + dir = -1; + dirf = REAL(-1.0); + } + + // compute: delta_x(C) = -dir*A(C,C)\A(C,i) + lcp.solve1 (delta_x,i,dir); + // note that delta_x[i] = dirf, but we wont bother to set it + + // compute: delta_w = A*delta_x ... note we only care about + // delta_w(N) and delta_w(i), the rest is ignored + lcp.pN_equals_ANC_times_qC (delta_w,delta_x); + lcp.pN_plusequals_ANi (delta_w,i,dir); + delta_w[i] = lcp.AiC_times_qC (i,delta_x) + lcp.Aii(i)*dirf; + + // find largest step we can take (size=s), either to drive x(i),w(i) + // to the valid LCP region or to drive an already-valid variable + // outside the valid region. + + int cmd = 1; // index switching command + int si = 0; // si = index to switch if cmd>3 + dReal s = -w[i]/delta_w[i]; + if (dir > 0) { + if (hi[i] < dInfinity) { + dReal s2 = (hi[i]-x[i])/dirf; // step to x(i)=hi(i) + if (s2 < s) { + s = s2; + cmd = 3; + } + } + } + else { + if (lo[i] > -dInfinity) { + dReal s2 = (lo[i]-x[i])/dirf; // step to x(i)=lo(i) + if (s2 < s) { + s = s2; + cmd = 2; + } + } + } + + for (k=0; k < lcp.numN(); k++) { + if ((state[lcp.indexN(k)]==0 && delta_w[lcp.indexN(k)] < 0) || + (state[lcp.indexN(k)]!=0 && delta_w[lcp.indexN(k)] > 0)) { + // don't bother checking if lo=hi=0 + if (lo[lcp.indexN(k)] == 0 && hi[lcp.indexN(k)] == 0) continue; + dReal s2 = -w[lcp.indexN(k)] / delta_w[lcp.indexN(k)]; + if (s2 < s) { + s = s2; + cmd = 4; + si = lcp.indexN(k); + } + } + } + + for (k=nub; k < lcp.numC(); k++) { + if (delta_x[lcp.indexC(k)] < 0 && lo[lcp.indexC(k)] > -dInfinity) { + dReal s2 = (lo[lcp.indexC(k)]-x[lcp.indexC(k)]) / + delta_x[lcp.indexC(k)]; + if (s2 < s) { + s = s2; + cmd = 5; + si = lcp.indexC(k); + } + } + if (delta_x[lcp.indexC(k)] > 0 && hi[lcp.indexC(k)] < dInfinity) { + dReal s2 = (hi[lcp.indexC(k)]-x[lcp.indexC(k)]) / + delta_x[lcp.indexC(k)]; + if (s2 < s) { + s = s2; + cmd = 6; + si = lcp.indexC(k); + } + } + } + + //static char* cmdstring[8] = {0,"->C","->NL","->NH","N->C", + // "C->NL","C->NH"}; + //printf ("cmd=%d (%s), si=%d\n",cmd,cmdstring[cmd],(cmd>3) ? si : i); + + // if s <= 0 then we've got a problem. if we just keep going then + // we're going to get stuck in an infinite loop. instead, just cross + // our fingers and exit with the current solution. + if (s <= 0) { + dMessage (d_ERR_LCP, "LCP internal error, s <= 0 (s=%.4e)",s); + if (i < (n-1)) { + dSetZero (x+i,n-i); + dSetZero (w+i,n-i); + } + goto done; + } + + // apply x = x + s * delta_x + lcp.pC_plusequals_s_times_qC (x,s,delta_x); + x[i] += s * dirf; + + // apply w = w + s * delta_w + lcp.pN_plusequals_s_times_qN (w,s,delta_w); + w[i] += s * delta_w[i]; + + // switch indexes between sets if necessary + switch (cmd) { + case 1: // done + w[i] = 0; + lcp.transfer_i_to_C (i); + break; + case 2: // done + x[i] = lo[i]; + state[i] = 0; + lcp.transfer_i_to_N (i); + break; + case 3: // done + x[i] = hi[i]; + state[i] = 1; + lcp.transfer_i_to_N (i); + break; + case 4: // keep going + w[si] = 0; + lcp.transfer_i_from_N_to_C (si); + break; + case 5: // keep going + x[si] = lo[si]; + state[si] = 0; + lcp.transfer_i_from_C_to_N (si); + break; + case 6: // keep going + x[si] = hi[si]; + state[si] = 1; + lcp.transfer_i_from_C_to_N (si); + break; + } + + if (cmd <= 3) break; + } + } + } + + done: + lcp.unpermute(); +} + +//*************************************************************************** +// accuracy and timing test + +extern "C" void dTestSolveLCP() +{ + int n = 100; + int i,nskip = dPAD(n); + const dReal tol = REAL(1e-9); + printf ("dTestSolveLCP()\n"); + + dReal *A = (dReal*) ALLOCA (n*nskip*sizeof(dReal)); + dReal *x = (dReal*) ALLOCA (n*sizeof(dReal)); + dReal *b = (dReal*) ALLOCA (n*sizeof(dReal)); + dReal *w = (dReal*) ALLOCA (n*sizeof(dReal)); + dReal *lo = (dReal*) ALLOCA (n*sizeof(dReal)); + dReal *hi = (dReal*) ALLOCA (n*sizeof(dReal)); + + dReal *A2 = (dReal*) ALLOCA (n*nskip*sizeof(dReal)); + dReal *b2 = (dReal*) ALLOCA (n*sizeof(dReal)); + dReal *lo2 = (dReal*) ALLOCA (n*sizeof(dReal)); + dReal *hi2 = (dReal*) ALLOCA (n*sizeof(dReal)); + dReal *tmp1 = (dReal*) ALLOCA (n*sizeof(dReal)); + dReal *tmp2 = (dReal*) ALLOCA (n*sizeof(dReal)); + + double total_time = 0; + for (int count=0; count < 1000; count++) { + + // form (A,b) = a random positive definite LCP problem + dMakeRandomMatrix (A2,n,n,1.0); + dMultiply2 (A,A2,A2,n,n,n); + dMakeRandomMatrix (x,n,1,1.0); + dMultiply0 (b,A,x,n,n,1); + for (i=0; i tol ? "FAILED" : "passed"); + if (diff > tol) dDebug (0,"A*x = b+w, maximum difference = %.6e",diff); + int n1=0,n2=0,n3=0; + for (i=0; i= 0) { + n1++; // ok + } + else if (x[i]==hi[i] && w[i] <= 0) { + n2++; // ok + } + else if (x[i] >= lo[i] && x[i] <= hi[i] && w[i] == 0) { + n3++; // ok + } + else { + dDebug (0,"FAILED: i=%d x=%.4e w=%.4e lo=%.4e hi=%.4e",i, + x[i],w[i],lo[i],hi[i]); + } + } + + // pacifier + printf ("passed: NL=%3d NH=%3d C=%3d ",n1,n2,n3); + printf ("time=%10.3f ms avg=%10.4f\n",time * 1000.0,average); + } +} diff --git a/HenocUniverse/ode/source/lcp.h b/HenocUniverse/ode/source/lcp.h new file mode 100755 index 0000000..484902c --- /dev/null +++ b/HenocUniverse/ode/source/lcp.h @@ -0,0 +1,58 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* + +given (A,b,lo,hi), solve the LCP problem: A*x = b+w, where each x(i),w(i) +satisfies one of + (1) x = lo, w >= 0 + (2) x = hi, w <= 0 + (3) lo < x < hi, w = 0 +A is a matrix of dimension n*n, everything else is a vector of size n*1. +lo and hi can be +/- dInfinity as needed. the first `nub' variables are +unbounded, i.e. hi and lo are assumed to be +/- dInfinity. + +we restrict lo(i) <= 0 and hi(i) >= 0. + +the original data (A,b) may be modified by this function. + +if the `findex' (friction index) parameter is nonzero, it points to an array +of index values. in this case constraints that have findex[i] >= 0 are +special. all non-special constraints are solved for, then the lo and hi values +for the special constraints are set: + hi[i] = abs( hi[i] * x[findex[i]] ) + lo[i] = -hi[i] +and the solution continues. this mechanism allows a friction approximation +to be implemented. the first `nub' variables are assumed to have findex < 0. + +*/ + + +#ifndef _ODE_LCP_H_ +#define _ODE_LCP_H_ + + +void dSolveLCP (int n, dReal *A, dReal *x, dReal *b, dReal *w, + int nub, dReal *lo, dReal *hi, int *findex); + + +#endif diff --git a/HenocUniverse/ode/source/mass.cpp b/HenocUniverse/ode/source/mass.cpp new file mode 100755 index 0000000..8832305 --- /dev/null +++ b/HenocUniverse/ode/source/mass.cpp @@ -0,0 +1,313 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#include +#include +#include +#include + + +#define _I(i,j) I[(i)*4+(j)] + + +// return 1 if ok, 0 if bad + +static int checkMass (dMass *m) +{ + int i; + + if (m->mass <= 0) { + dDEBUGMSG ("mass must be > 0"); + return 0; + } + if (!dIsPositiveDefinite (m->I,3)) { + dDEBUGMSG ("inertia must be positive definite"); + return 0; + } + + // verify that the center of mass position is consistent with the mass + // and inertia matrix. this is done by checking that the inertia around + // the center of mass is also positive definite. from the comment in + // dMassTranslate(), if the body is translated so that its center of mass + // is at the point of reference, then the new inertia is: + // I + mass*crossmat(c)^2 + // note that requiring this to be positive definite is exactly equivalent + // to requiring that the spatial inertia matrix + // [ mass*eye(3,3) M*crossmat(c)^T ] + // [ M*crossmat(c) I ] + // is positive definite, given that I is PD and mass>0. see the theorem + // about partitioned PD matrices for proof. + + dMatrix3 I2,chat; + dSetZero (chat,12); + dCROSSMAT (chat,m->c,4,+,-); + dMULTIPLY0_333 (I2,chat,chat); + for (i=0; i<3; i++) I2[i] = m->I[i] + m->mass*I2[i]; + for (i=4; i<7; i++) I2[i] = m->I[i] + m->mass*I2[i]; + for (i=8; i<11; i++) I2[i] = m->I[i] + m->mass*I2[i]; + if (!dIsPositiveDefinite (I2,3)) { + dDEBUGMSG ("center of mass inconsistent with mass parameters"); + return 0; + } + return 1; +} + + +void dMassSetZero (dMass *m) +{ + dAASSERT (m); + m->mass = REAL(0.0); + dSetZero (m->c,sizeof(m->c) / sizeof(dReal)); + dSetZero (m->I,sizeof(m->I) / sizeof(dReal)); +} + + +void dMassSetParameters (dMass *m, dReal themass, + dReal cgx, dReal cgy, dReal cgz, + dReal I11, dReal I22, dReal I33, + dReal I12, dReal I13, dReal I23) +{ + dAASSERT (m); + dMassSetZero (m); + m->mass = themass; + m->c[0] = cgx; + m->c[1] = cgy; + m->c[2] = cgz; + m->_I(0,0) = I11; + m->_I(1,1) = I22; + m->_I(2,2) = I33; + m->_I(0,1) = I12; + m->_I(0,2) = I13; + m->_I(1,2) = I23; + m->_I(1,0) = I12; + m->_I(2,0) = I13; + m->_I(2,1) = I23; + checkMass (m); +} + + +void dMassSetSphere (dMass *m, dReal density, dReal radius) +{ + dMassSetSphereTotal (m, (REAL(4.0)/REAL(3.0)) * M_PI * + radius*radius*radius * density, radius); +} + + +void dMassSetSphereTotal (dMass *m, dReal total_mass, dReal radius) +{ + dAASSERT (m); + dMassSetZero (m); + m->mass = total_mass; + dReal II = REAL(0.4) * total_mass * radius*radius; + m->_I(0,0) = II; + m->_I(1,1) = II; + m->_I(2,2) = II; + +# ifndef dNODEBUG + checkMass (m); +# endif +} + + +void dMassSetCappedCylinder (dMass *m, dReal density, int direction, + dReal radius, dReal length) +{ + dReal M1,M2,Ia,Ib; + dAASSERT (m); + dUASSERT (direction >= 1 && direction <= 3,"bad direction number"); + dMassSetZero (m); + M1 = M_PI*radius*radius*length*density; // cylinder mass + M2 = (REAL(4.0)/REAL(3.0))*M_PI*radius*radius*radius*density; // total cap mass + m->mass = M1+M2; + Ia = M1*(REAL(0.25)*radius*radius + (REAL(1.0)/REAL(12.0))*length*length) + + M2*(REAL(0.4)*radius*radius + REAL(0.375)*radius*length + REAL(0.25)*length*length); + Ib = (M1*REAL(0.5) + M2*REAL(0.4))*radius*radius; + m->_I(0,0) = Ia; + m->_I(1,1) = Ia; + m->_I(2,2) = Ia; + m->_I(direction-1,direction-1) = Ib; + +# ifndef dNODEBUG + checkMass (m); +# endif +} + + +void dMassSetCappedCylinderTotal (dMass *m, dReal total_mass, int direction, + dReal a, dReal b) +{ + dMassSetCappedCylinder (m, 1.0, direction, a, b); + dMassAdjust (m, total_mass); +} + + +void dMassSetCylinder (dMass *m, dReal density, int direction, + dReal radius, dReal length) +{ + dMassSetCylinderTotal (m, M_PI*radius*radius*length*density, + direction, radius, length); +} + +void dMassSetCylinderTotal (dMass *m, dReal total_mass, int direction, + dReal radius, dReal length) +{ + dReal r2,I; + dAASSERT (m); + dMassSetZero (m); + r2 = radius*radius; + m->mass = total_mass; + I = total_mass*(REAL(0.25)*r2 + (REAL(1.0)/REAL(12.0))*length*length); + m->_I(0,0) = I; + m->_I(1,1) = I; + m->_I(2,2) = I; + m->_I(direction-1,direction-1) = total_mass*REAL(0.5)*r2; + +# ifndef dNODEBUG + checkMass (m); +# endif +} + + +void dMassSetBox (dMass *m, dReal density, + dReal lx, dReal ly, dReal lz) +{ + dMassSetBoxTotal (m, lx*ly*lz*density, lx, ly, lz); +} + + +void dMassSetBoxTotal (dMass *m, dReal total_mass, + dReal lx, dReal ly, dReal lz) +{ + dAASSERT (m); + dMassSetZero (m); + m->mass = total_mass; + m->_I(0,0) = total_mass/REAL(12.0) * (ly*ly + lz*lz); + m->_I(1,1) = total_mass/REAL(12.0) * (lx*lx + lz*lz); + m->_I(2,2) = total_mass/REAL(12.0) * (lx*lx + ly*ly); + +# ifndef dNODEBUG + checkMass (m); +# endif +} + + +void dMassAdjust (dMass *m, dReal newmass) +{ + dAASSERT (m); + dReal scale = newmass / m->mass; + m->mass = newmass; + for (int i=0; i<3; i++) for (int j=0; j<3; j++) m->_I(i,j) *= scale; + +# ifndef dNODEBUG + checkMass (m); +# endif +} + + +void dMassTranslate (dMass *m, dReal x, dReal y, dReal z) +{ + // if the body is translated by `a' relative to its point of reference, + // the new inertia about the point of reference is: + // + // I + mass*(crossmat(c)^2 - crossmat(c+a)^2) + // + // where c is the existing center of mass and I is the old inertia. + + int i,j; + dMatrix3 ahat,chat,t1,t2; + dReal a[3]; + + dAASSERT (m); + + // adjust inertia matrix + dSetZero (chat,12); + dCROSSMAT (chat,m->c,4,+,-); + a[0] = x + m->c[0]; + a[1] = y + m->c[1]; + a[2] = z + m->c[2]; + dSetZero (ahat,12); + dCROSSMAT (ahat,a,4,+,-); + dMULTIPLY0_333 (t1,ahat,ahat); + dMULTIPLY0_333 (t2,chat,chat); + for (i=0; i<3; i++) for (j=0; j<3; j++) + m->_I(i,j) += m->mass * (t2[i*4+j]-t1[i*4+j]); + + // ensure perfect symmetry + m->_I(1,0) = m->_I(0,1); + m->_I(2,0) = m->_I(0,2); + m->_I(2,1) = m->_I(1,2); + + // adjust center of mass + m->c[0] += x; + m->c[1] += y; + m->c[2] += z; + +# ifndef dNODEBUG + checkMass (m); +# endif +} + + +void dMassRotate (dMass *m, const dMatrix3 R) +{ + // if the body is rotated by `R' relative to its point of reference, + // the new inertia about the point of reference is: + // + // R * I * R' + // + // where I is the old inertia. + + dMatrix3 t1; + dReal t2[3]; + + dAASSERT (m); + + // rotate inertia matrix + dMULTIPLY2_333 (t1,m->I,R); + dMULTIPLY0_333 (m->I,R,t1); + + // ensure perfect symmetry + m->_I(1,0) = m->_I(0,1); + m->_I(2,0) = m->_I(0,2); + m->_I(2,1) = m->_I(1,2); + + // rotate center of mass + dMULTIPLY0_331 (t2,R,m->c); + m->c[0] = t2[0]; + m->c[1] = t2[1]; + m->c[2] = t2[2]; + +# ifndef dNODEBUG + checkMass (m); +# endif +} + + +void dMassAdd (dMass *a, const dMass *b) +{ + int i; + dAASSERT (a && b); + dReal denom = dRecip (a->mass + b->mass); + for (i=0; i<3; i++) a->c[i] = (a->c[i]*a->mass + b->c[i]*b->mass)*denom; + a->mass += b->mass; + for (i=0; i<12; i++) a->I[i] += b->I[i]; +} diff --git a/HenocUniverse/ode/source/mat.cpp b/HenocUniverse/ode/source/mat.cpp new file mode 100755 index 0000000..6e635dc --- /dev/null +++ b/HenocUniverse/ode/source/mat.cpp @@ -0,0 +1,230 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#include +#include +#include +#include +#include +#include "mat.h" + + +dMatrix::dMatrix() +{ + n = 0; + m = 0; + data = 0; +} + + +dMatrix::dMatrix (int rows, int cols) +{ + if (rows < 1 || cols < 1) dDebug (0,"bad matrix size"); + n = rows; + m = cols; + data = (dReal*) dAlloc (n*m*sizeof(dReal)); + dSetZero (data,n*m); +} + + +dMatrix::dMatrix (const dMatrix &a) +{ + n = a.n; + m = a.m; + data = (dReal*) dAlloc (n*m*sizeof(dReal)); + memcpy (data,a.data,n*m*sizeof(dReal)); +} + + +dMatrix::dMatrix (int rows, int cols, + dReal *_data, int rowskip, int colskip) +{ + if (rows < 1 || cols < 1) dDebug (0,"bad matrix size"); + n = rows; + m = cols; + data = (dReal*) dAlloc (n*m*sizeof(dReal)); + for (int i=0; i= n || j < 0 || j >= m) dDebug (0,"bad matrix (i,j)"); + return data [i*m+j]; +} + + +void dMatrix::operator= (const dMatrix &a) +{ + if (data) dFree (data,n*m*sizeof(dReal)); + n = a.n; + m = a.m; + if (n > 0 && m > 0) { + data = (dReal*) dAlloc (n*m*sizeof(dReal)); + memcpy (data,a.data,n*m*sizeof(dReal)); + } + else data = 0; +} + + +void dMatrix::operator= (dReal a) +{ + for (int i=0; i= n || q[i] < 0 || q[i] >= m) + dDebug (0,"Matrix select, bad index arrays"); + r.data[i*nq+j] = data[p[i]*m+q[j]]; + } + } + return r; +} + + +dMatrix dMatrix::operator + (const dMatrix &a) +{ + if (n != a.n || m != a.m) dDebug (0,"matrix +, mismatched sizes"); + dMatrix r (n,m); + for (int i=0; i max) max = diff; + } + } + return max; +} diff --git a/HenocUniverse/ode/source/mat.h b/HenocUniverse/ode/source/mat.h new file mode 100755 index 0000000..2814a01 --- /dev/null +++ b/HenocUniverse/ode/source/mat.h @@ -0,0 +1,71 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +// matrix class. this is mostly for convenience in the testing code, it is +// not optimized at all. correctness is much more importance here. + +#ifndef _ODE_MAT_H_ +#define _ODE_MAT_H_ + +#include + + +class dMatrix { + int n,m; // matrix dimension, n,m >= 0 + dReal *data; // if nonzero, n*m elements allocated on the heap + +public: + // constructors, destructors + dMatrix(); // make default 0x0 matrix + dMatrix (int rows, int cols); // construct zero matrix of given size + dMatrix (const dMatrix &); // construct copy of given matrix + // create copy of given data - element (i,j) is data[i*rowskip+j*colskip] + dMatrix (int rows, int cols, dReal *_data, int rowskip, int colskip); + ~dMatrix(); // destructor + + // data movement + dReal & operator () (int i, int j); // reference an element + void operator= (const dMatrix &); // matrix = matrix + void operator= (dReal); // matrix = scalar + dMatrix transpose(); // return transposed matrix + // return a permuted submatrix of this matrix, made up of the rows in p + // and the columns in q. p has np elements, q has nq elements. + dMatrix select (int np, int *p, int nq, int *q); + + // operators + dMatrix operator + (const dMatrix &); + dMatrix operator - (const dMatrix &); + dMatrix operator - (); + dMatrix operator * (const dMatrix &); + void operator += (const dMatrix &); + void operator -= (const dMatrix &); + + // utility + void clearUpperTriangle(); + void clearLowerTriangle(); + void makeRandom (dReal range); + void print (char *fmt = "%10.4f ", FILE *f=stdout); + dReal maxDifference (const dMatrix &); +}; + + +#endif diff --git a/HenocUniverse/ode/source/matrix.cpp b/HenocUniverse/ode/source/matrix.cpp new file mode 100755 index 0000000..0d46402 --- /dev/null +++ b/HenocUniverse/ode/source/matrix.cpp @@ -0,0 +1,360 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#include +#include +#include +#include + +// misc defines +#define ALLOCA dALLOCA16 + + +void dSetZero (dReal *a, int n) +{ + dAASSERT (a && n >= 0); + while (n > 0) { + *(a++) = 0; + n--; + } +} + + +void dSetValue (dReal *a, int n, dReal value) +{ + dAASSERT (a && n >= 0); + while (n > 0) { + *(a++) = value; + n--; + } +} + + +void dMultiply0 (dReal *A, const dReal *B, const dReal *C, int p, int q, int r) +{ + int i,j,k,qskip,rskip,rpad; + dAASSERT (A && B && C && p>0 && q>0 && r>0); + qskip = dPAD(q); + rskip = dPAD(r); + rpad = rskip - r; + dReal sum; + const dReal *b,*c,*bb; + bb = B; + for (i=p; i; i--) { + for (j=0 ; j0 && q>0 && r>0); + pskip = dPAD(p); + rskip = dPAD(r); + for (i=0; i0 && q>0 && r>0); + rpad = dPAD(r) - r; + qskip = dPAD(q); + bb = B; + for (i=p; i; i--) { + cc = C; + for (j=r; j; j--) { + z = 0; + sum = 0; + for (k=q; k; k--,z++) sum += bb[z] * cc[z]; + *(A++) = sum; + cc += qskip; + } + A += rpad; + bb += qskip; + } +} + + +int dFactorCholesky (dReal *A, int n) +{ + int i,j,k,nskip; + dReal sum,*a,*b,*aa,*bb,*cc,*recip; + dAASSERT (n > 0 && A); + nskip = dPAD (n); + recip = (dReal*) ALLOCA (n * sizeof(dReal)); + aa = A; + for (i=0; i 0 && L && b); + nskip = dPAD (n); + y = (dReal*) ALLOCA (n*sizeof(dReal)); + for (i=0; i= 0; i--) { + sum = 0; + for (k=i+1; k < n; k++) sum += L[k*nskip+i]*b[k]; + b[i] = (y[i]-sum)/L[i*nskip+i]; + } +} + + +int dInvertPDMatrix (const dReal *A, dReal *Ainv, int n) +{ + int i,j,nskip; + dReal *L,*x; + dAASSERT (n > 0 && A && Ainv); + nskip = dPAD (n); + L = (dReal*) ALLOCA (nskip*n*sizeof(dReal)); + memcpy (L,A,nskip*n*sizeof(dReal)); + x = (dReal*) ALLOCA (n*sizeof(dReal)); + if (dFactorCholesky (L,n)==0) return 0; + dSetZero (Ainv,n*nskip); // make sure all padding elements set to 0 + for (i=0; i 0 && A); + int nskip = dPAD (n); + Acopy = (dReal*) ALLOCA (nskip*n * sizeof(dReal)); + memcpy (Acopy,A,nskip*n * sizeof(dReal)); + return dFactorCholesky (Acopy,n); +} + + +/***** this has been replaced by a faster version +void dSolveL1T (const dReal *L, dReal *b, int n, int nskip) +{ + int i,j; + dAASSERT (L && b && n >= 0 && nskip >= n); + dReal sum; + for (i=n-2; i>=0; i--) { + sum = 0; + for (j=i+1; j= 0); + for (int i=0; i 0 && nskip >= n); + dSolveL1 (L,b,n,nskip); + dVectorScale (b,d,n); + dSolveL1T (L,b,n,nskip); +} + + +void dLDLTAddTL (dReal *L, dReal *d, const dReal *a, int n, int nskip) +{ + int j,p; + dReal *W1,*W2,W11,W21,alpha1,alpha2,alphanew,gamma1,gamma2,k1,k2,Wp,ell,dee; + dAASSERT (L && d && a && n > 0 && nskip >= n); + + if (n < 2) return; + W1 = (dReal*) ALLOCA (n*sizeof(dReal)); + W2 = (dReal*) ALLOCA (n*sizeof(dReal)); + + W1[0] = 0; + W2[0] = 0; + for (j=1; j j) ? _GETA(i,j) : _GETA(j,i)) + + +void dLDLTRemove (dReal **A, const int *p, dReal *L, dReal *d, + int n1, int n2, int r, int nskip) +{ + int i; + dAASSERT(A && p && L && d && n1 > 0 && n2 > 0 && r >= 0 && r < n2 && + n1 >= n2 && nskip >= n1); + #ifndef dNODEBUG + for (i=0; i= 0 && p[i] < n1); + #endif + + if (r==n2-1) { + return; // deleting last row/col is easy + } + else if (r==0) { + dReal *a = (dReal*) ALLOCA (n2 * sizeof(dReal)); + for (i=0; i 0 && nskip >= n && r >= 0 && r < n); + if (r >= n-1) return; + if (r > 0) { + for (i=0; i +#include +#include +#include + + +static dAllocFunction *allocfn = 0; +static dReallocFunction *reallocfn = 0; +static dFreeFunction *freefn = 0; + + + +void dSetAllocHandler (dAllocFunction *fn) +{ + allocfn = fn; +} + + +void dSetReallocHandler (dReallocFunction *fn) +{ + reallocfn = fn; +} + + +void dSetFreeHandler (dFreeFunction *fn) +{ + freefn = fn; +} + + +dAllocFunction *dGetAllocHandler() +{ + return allocfn; +} + + +dReallocFunction *dGetReallocHandler() +{ + return reallocfn; +} + + +dFreeFunction *dGetFreeHandler() +{ + return freefn; +} + + +void * dAlloc (size_t size) +{ + if (allocfn) return allocfn (size); else return malloc (size); +} + + +void * dRealloc (void *ptr, size_t oldsize, size_t newsize) +{ + if (reallocfn) return reallocfn (ptr,oldsize,newsize); + else return realloc (ptr,newsize); +} + + +void dFree (void *ptr, size_t size) +{ + if (!ptr) return; + if (freefn) freefn (ptr,size); else free (ptr); +} diff --git a/HenocUniverse/ode/source/misc.cpp b/HenocUniverse/ode/source/misc.cpp new file mode 100755 index 0000000..08453c4 --- /dev/null +++ b/HenocUniverse/ode/source/misc.cpp @@ -0,0 +1,147 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#include +#include +#include + +//**************************************************************************** +// random numbers + +static unsigned long seed = 0; + +unsigned long dRand() +{ + seed = (1664525L*seed + 1013904223L) & 0xffffffff; + return seed; +} + + +unsigned long dRandGetSeed() +{ + return seed; +} + + +void dRandSetSeed (unsigned long s) +{ + seed = s; +} + + +int dTestRand() +{ + unsigned long oldseed = seed; + int ret = 1; + seed = 0; + if (dRand() != 0x3c6ef35f || dRand() != 0x47502932 || + dRand() != 0xd1ccf6e9 || dRand() != 0xaaf95334 || + dRand() != 0x6252e503) ret = 0; + seed = oldseed; + return ret; +} + + +int dRandInt (int n) +{ + double a = double(n) / 4294967296.0; + return (int) (double(dRand()) * a); +} + + +dReal dRandReal() +{ + return ((dReal) dRand()) / ((dReal) 0xffffffff); +} + +//**************************************************************************** +// matrix utility stuff + +void dPrintMatrix (const dReal *A, int n, int m, char *fmt, FILE *f) +{ + int i,j; + int skip = dPAD(m); + for (i=0; i max) max = diff; + } + } + return max; +} + + +dReal dMaxDifferenceLowerTriangle (const dReal *A, const dReal *B, int n) +{ + int i,j; + int skip = dPAD(n); + dReal diff,max; + max = 0; + for (i=0; i max) max = diff; + } + } + return max; +} diff --git a/HenocUniverse/ode/source/objects.h b/HenocUniverse/ode/source/objects.h new file mode 100755 index 0000000..7475ac2 --- /dev/null +++ b/HenocUniverse/ode/source/objects.h @@ -0,0 +1,125 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +// object, body, and world structs. + + +#ifndef _ODE_OBJECT_H_ +#define _ODE_OBJECT_H_ + +#include +#include +#include +#include "array.h" + + +// some body flags + +enum { + dxBodyFlagFiniteRotation = 1, // use finite rotations + dxBodyFlagFiniteRotationAxis = 2, // use finite rotations only along axis + dxBodyDisabled = 4, // body is disabled + dxBodyNoGravity = 8, // body is not influenced by gravity + dxBodyAutoDisable = 16 // enable auto-disable on body +}; + + +// base class that does correct object allocation / deallocation + +struct dBase { + void *operator new (size_t size) { return dAlloc (size); } + void operator delete (void *ptr, size_t size) { dFree (ptr,size); } + void *operator new[] (size_t size) { return dAlloc (size); } + void operator delete[] (void *ptr, size_t size) { dFree (ptr,size); } +}; + + +// base class for bodies and joints + +struct dObject : public dBase { + dxWorld *world; // world this object is in + dObject *next; // next object of this type in list + dObject **tome; // pointer to previous object's next ptr + void *userdata; // user settable data + int tag; // used by dynamics algorithms +}; + + +// auto disable parameters +struct dxAutoDisable { + dReal linear_threshold; // linear (squared) velocity treshold + dReal angular_threshold; // angular (squared) velocity treshold + dReal idle_time; // time the body needs to be idle to auto-disable it + int idle_steps; // steps the body needs to be idle to auto-disable it +}; + + +// quick-step parameters +struct dxQuickStepParameters { + int num_iterations; // number of SOR iterations to perform + dReal w; // the SOR over-relaxation parameter +}; + + +// contact generation parameters +struct dxContactParameters { + dReal max_vel; // maximum correcting velocity + dReal min_depth; // thickness of 'surface layer' +}; + + +struct dxBody : public dObject { + dxJointNode *firstjoint; // list of attached joints + int flags; // some dxBodyFlagXXX flags + dGeomID geom; // first collision geom associated with body + dMass mass; // mass parameters about POR + dMatrix3 invI; // inverse of mass.I + dReal invMass; // 1 / mass.mass + dVector3 pos; // position of POR (point of reference) + dQuaternion q; // orientation quaternion + dMatrix3 R; // rotation matrix, always corresponds to q + dVector3 lvel,avel; // linear and angular velocity of POR + dVector3 facc,tacc; // force and torque accumulators + dVector3 finite_rot_axis; // finite rotation axis, unit length or 0=none + + // auto-disable information + dxAutoDisable adis; // auto-disable parameters + dReal adis_timeleft; // time left to be idle + int adis_stepsleft; // steps left to be idle +}; + + +struct dxWorld : public dBase { + dxBody *firstbody; // body linked list + dxJoint *firstjoint; // joint linked list + int nb,nj; // number of bodies and joints in lists + dVector3 gravity; // gravity vector (m/s/s) + dReal global_erp; // global error reduction parameter + dReal global_cfm; // global costraint force mixing parameter + dxAutoDisable adis; // auto-disable parameters + int adis_flag; // auto-disable flag for new bodies + dxQuickStepParameters qs; + dxContactParameters contactp; +}; + + +#endif diff --git a/HenocUniverse/ode/source/obstack.cpp b/HenocUniverse/ode/source/obstack.cpp new file mode 100755 index 0000000..0840396 --- /dev/null +++ b/HenocUniverse/ode/source/obstack.cpp @@ -0,0 +1,130 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#include +#include +#include +#include "obstack.h" + +//**************************************************************************** +// macros and constants + +#define ROUND_UP_OFFSET_TO_EFFICIENT_SIZE(arena,ofs) \ + ofs = (size_t) (dEFFICIENT_SIZE( ((intP)(arena)) + ofs ) - ((intP)(arena)) ); + +#define MAX_ALLOC_SIZE \ + ((size_t)(dOBSTACK_ARENA_SIZE - sizeof (Arena) - EFFICIENT_ALIGNMENT + 1)) + +//**************************************************************************** +// dObStack + +dObStack::dObStack() +{ + first = 0; + last = 0; + current_arena = 0; + current_ofs = 0; +} + + +dObStack::~dObStack() +{ + // free all arenas + Arena *a,*nexta; + a = first; + while (a) { + nexta = a->next; + dFree (a,dOBSTACK_ARENA_SIZE); + a = nexta; + } +} + + +void *dObStack::alloc (int num_bytes) +{ + if ((size_t)num_bytes > MAX_ALLOC_SIZE) dDebug (0,"num_bytes too large"); + + // allocate or move to a new arena if necessary + if (!first) { + // allocate the first arena if necessary + first = last = (Arena *) dAlloc (dOBSTACK_ARENA_SIZE); + first->next = 0; + first->used = sizeof (Arena); + ROUND_UP_OFFSET_TO_EFFICIENT_SIZE (first,first->used); + } + else { + // we already have one or more arenas, see if a new arena must be used + if ((last->used + num_bytes) > dOBSTACK_ARENA_SIZE) { + if (!last->next) { + last->next = (Arena *) dAlloc (dOBSTACK_ARENA_SIZE); + last->next->next = 0; + } + last = last->next; + last->used = sizeof (Arena); + ROUND_UP_OFFSET_TO_EFFICIENT_SIZE (last,last->used); + } + } + + // allocate an area in the arena + char *c = ((char*) last) + last->used; + last->used += num_bytes; + ROUND_UP_OFFSET_TO_EFFICIENT_SIZE (last,last->used); + return c; +} + + +void dObStack::freeAll() +{ + last = first; + if (first) { + first->used = sizeof(Arena); + ROUND_UP_OFFSET_TO_EFFICIENT_SIZE (first,first->used); + } +} + + +void *dObStack::rewind() +{ + current_arena = first; + current_ofs = sizeof (Arena); + if (current_arena) { + ROUND_UP_OFFSET_TO_EFFICIENT_SIZE (current_arena,current_ofs) + return ((char*) current_arena) + current_ofs; + } + else return 0; +} + + +void *dObStack::next (int num_bytes) +{ + // this functions like alloc, except that no new storage is ever allocated + if (!current_arena) return 0; + current_ofs += num_bytes; + ROUND_UP_OFFSET_TO_EFFICIENT_SIZE (current_arena,current_ofs); + if (current_ofs >= current_arena->used) { + current_arena = current_arena->next; + if (!current_arena) return 0; + current_ofs = sizeof (Arena); + ROUND_UP_OFFSET_TO_EFFICIENT_SIZE (current_arena,current_ofs); + } + return ((char*) current_arena) + current_ofs; +} diff --git a/HenocUniverse/ode/source/obstack.h b/HenocUniverse/ode/source/obstack.h new file mode 100755 index 0000000..fd283fe --- /dev/null +++ b/HenocUniverse/ode/source/obstack.h @@ -0,0 +1,68 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef _ODE_OBSTACK_H_ +#define _ODE_OBSTACK_H_ + +#include "objects.h" + +// each obstack Arena pointer points to a block of this many bytes +#define dOBSTACK_ARENA_SIZE 16384 + + +struct dObStack : public dBase { + struct Arena { + Arena *next; // next arena in linked list + int used; // total number of bytes used in this arena, counting + }; // this header + + Arena *first; // head of the arena linked list. 0 if no arenas yet + Arena *last; // arena where blocks are currently being allocated + + // used for iterator + Arena *current_arena; + int current_ofs; + + dObStack(); + ~dObStack(); + + void *alloc (int num_bytes); + // allocate a block in the last arena, allocating a new arena if necessary. + // it is a runtime error if num_bytes is larger than the arena size. + + void freeAll(); + // free all blocks in all arenas. this does not deallocate the arenas + // themselves, so future alloc()s will reuse them. + + void *rewind(); + // rewind the obstack iterator, and return the address of the first + // allocated block. return 0 if there are no allocated blocks. + + void *next (int num_bytes); + // return the address of the next allocated block. 'num_bytes' is the size + // of the previous block. this returns null if there are no more arenas. + // the sequence of 'num_bytes' parameters passed to next() during a + // traversal of the list must exactly match the parameters passed to alloc(). +}; + + +#endif diff --git a/HenocUniverse/ode/source/ode.cpp b/HenocUniverse/ode/source/ode.cpp new file mode 100755 index 0000000..4e75cac --- /dev/null +++ b/HenocUniverse/ode/source/ode.cpp @@ -0,0 +1,1538 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifdef _MSC_VER +#pragma warning(disable:4291) // for VC++, no complaints about "no matching operator delete found" +#endif + +// this source file is mostly concerned with the data structures, not the +// numerics. + +#include "objects.h" +#include +#include "joint.h" +#include +#include +#include "step.h" +#include "quickstep.h" +#include "util.h" +#include +#include +#include +#include + +// misc defines +#define ALLOCA dALLOCA16 + +//**************************************************************************** +// utility + +static inline void initObject (dObject *obj, dxWorld *w) +{ + obj->world = w; + obj->next = 0; + obj->tome = 0; + obj->userdata = 0; + obj->tag = 0; +} + + +// add an object `obj' to the list who's head pointer is pointed to by `first'. + +static inline void addObjectToList (dObject *obj, dObject **first) +{ + obj->next = *first; + obj->tome = first; + if (*first) (*first)->tome = &obj->next; + (*first) = obj; +} + + +// remove the object from the linked list + +static inline void removeObjectFromList (dObject *obj) +{ + if (obj->next) obj->next->tome = obj->tome; + *(obj->tome) = obj->next; + // safeguard + obj->next = 0; + obj->tome = 0; +} + + +// remove the joint from neighbour lists of all connected bodies + +static void removeJointReferencesFromAttachedBodies (dxJoint *j) +{ + for (int i=0; i<2; i++) { + dxBody *body = j->node[i].body; + if (body) { + dxJointNode *n = body->firstjoint; + dxJointNode *last = 0; + while (n) { + if (n->joint == j) { + if (last) last->next = n->next; + else body->firstjoint = n->next; + break; + } + last = n; + n = n->next; + } + } + } + j->node[0].body = 0; + j->node[0].next = 0; + j->node[1].body = 0; + j->node[1].next = 0; +} + +//**************************************************************************** +// debugging + +// see if an object list loops on itself (if so, it's bad). + +static int listHasLoops (dObject *first) +{ + if (first==0 || first->next==0) return 0; + dObject *a=first,*b=first->next; + int skip=0; + while (b) { + if (a==b) return 1; + b = b->next; + if (skip) a = a->next; + skip ^= 1; + } + return 0; +} + + +// check the validity of the world data structures + +static void checkWorld (dxWorld *w) +{ + dxBody *b; + dxJoint *j; + + // check there are no loops + if (listHasLoops (w->firstbody)) dDebug (0,"body list has loops"); + if (listHasLoops (w->firstjoint)) dDebug (0,"joint list has loops"); + + // check lists are well formed (check `tome' pointers) + for (b=w->firstbody; b; b=(dxBody*)b->next) { + if (b->next && b->next->tome != &b->next) + dDebug (0,"bad tome pointer in body list"); + } + for (j=w->firstjoint; j; j=(dxJoint*)j->next) { + if (j->next && j->next->tome != &j->next) + dDebug (0,"bad tome pointer in joint list"); + } + + // check counts + int n = 0; + for (b=w->firstbody; b; b=(dxBody*)b->next) n++; + if (w->nb != n) dDebug (0,"body count incorrect"); + n = 0; + for (j=w->firstjoint; j; j=(dxJoint*)j->next) n++; + if (w->nj != n) dDebug (0,"joint count incorrect"); + + // set all tag values to a known value + static int count = 0; + count++; + for (b=w->firstbody; b; b=(dxBody*)b->next) b->tag = count; + for (j=w->firstjoint; j; j=(dxJoint*)j->next) j->tag = count; + + // check all body/joint world pointers are ok + for (b=w->firstbody; b; b=(dxBody*)b->next) if (b->world != w) + dDebug (0,"bad world pointer in body list"); + for (j=w->firstjoint; j; j=(dxJoint*)j->next) if (j->world != w) + dDebug (0,"bad world pointer in joint list"); + + /* + // check for half-connected joints - actually now these are valid + for (j=w->firstjoint; j; j=(dxJoint*)j->next) { + if (j->node[0].body || j->node[1].body) { + if (!(j->node[0].body && j->node[1].body)) + dDebug (0,"half connected joint found"); + } + } + */ + + // check that every joint node appears in the joint lists of both bodies it + // attaches + for (j=w->firstjoint; j; j=(dxJoint*)j->next) { + for (int i=0; i<2; i++) { + if (j->node[i].body) { + int ok = 0; + for (dxJointNode *n=j->node[i].body->firstjoint; n; n=n->next) { + if (n->joint == j) ok = 1; + } + if (ok==0) dDebug (0,"joint not in joint list of attached body"); + } + } + } + + // check all body joint lists (correct body ptrs) + for (b=w->firstbody; b; b=(dxBody*)b->next) { + for (dxJointNode *n=b->firstjoint; n; n=n->next) { + if (&n->joint->node[0] == n) { + if (n->joint->node[1].body != b) + dDebug (0,"bad body pointer in joint node of body list (1)"); + } + else { + if (n->joint->node[0].body != b) + dDebug (0,"bad body pointer in joint node of body list (2)"); + } + if (n->joint->tag != count) dDebug (0,"bad joint node pointer in body"); + } + } + + // check all body pointers in joints, check they are distinct + for (j=w->firstjoint; j; j=(dxJoint*)j->next) { + if (j->node[0].body && (j->node[0].body == j->node[1].body)) + dDebug (0,"non-distinct body pointers in joint"); + if ((j->node[0].body && j->node[0].body->tag != count) || + (j->node[1].body && j->node[1].body->tag != count)) + dDebug (0,"bad body pointer in joint"); + } +} + + +void dWorldCheck (dxWorld *w) +{ + checkWorld (w); +} + +//**************************************************************************** +// body + +dxBody *dBodyCreate (dxWorld *w) +{ + dAASSERT (w); + dxBody *b = new dxBody; + initObject (b,w); + b->firstjoint = 0; + b->flags = 0; + b->geom = 0; + dMassSetParameters (&b->mass,1,0,0,0,1,1,1,0,0,0); + dSetZero (b->invI,4*3); + b->invI[0] = 1; + b->invI[5] = 1; + b->invI[10] = 1; + b->invMass = 1; + dSetZero (b->pos,4); + dSetZero (b->q,4); + b->q[0] = 1; + dRSetIdentity (b->R); + dSetZero (b->lvel,4); + dSetZero (b->avel,4); + dSetZero (b->facc,4); + dSetZero (b->tacc,4); + dSetZero (b->finite_rot_axis,4); + addObjectToList (b,(dObject **) &w->firstbody); + w->nb++; + + // set auto-disable parameters + dBodySetAutoDisableDefaults (b); // must do this after adding to world + b->adis_stepsleft = b->adis.idle_steps; + b->adis_timeleft = b->adis.idle_time; + + return b; +} + + +void dBodyDestroy (dxBody *b) +{ + dAASSERT (b); + +/* // Flatland does not need to do this. + + // all geoms that link to this body must be notified that the body is about + // to disappear. note that the call to dGeomSetBody(geom,0) will result in + // dGeomGetBodyNext() returning 0 for the body, so we must get the next body + // before setting the body to 0. + dxGeom *next_geom = 0; + for (dxGeom *geom = b->geom; geom; geom = next_geom) { + next_geom = dGeomGetBodyNext (geom); + dGeomSetBody (geom,0); + } +*/ + + // detach all neighbouring joints, then delete this body. + dxJointNode *n = b->firstjoint; + while (n) { + // sneaky trick to speed up removal of joint references (black magic) + n->joint->node[(n == n->joint->node)].body = 0; + + dxJointNode *next = n->next; + n->next = 0; + removeJointReferencesFromAttachedBodies (n->joint); + n = next; + } + removeObjectFromList (b); + b->world->nb--; + delete b; +} + + +void dBodySetData (dBodyID b, void *data) +{ + dAASSERT (b); + b->userdata = data; +} + + +void *dBodyGetData (dBodyID b) +{ + dAASSERT (b); + return b->userdata; +} + + +void dBodySetPosition (dBodyID b, dReal x, dReal y, dReal z) +{ + dAASSERT (b); + b->pos[0] = x; + b->pos[1] = y; + b->pos[2] = z; + + // notify all attached geoms that this body has moved + for (dxGeom *geom = b->geom; geom; geom = dGeomGetBodyNext (geom)) + dGeomMoved (geom); +} + +void dBodyInit(dBodyID b, dReal x, dReal y, const dMatrix3 R) +{ + dAASSERT (b && R); + + b->pos[0] = x; + b->pos[1] = y; + b->pos[2] = 0; + + dQuaternion q; + dRtoQ (R,q); + dNormalize4 (q); + b->q[0] = q[0]; + b->q[1] = q[1]; + b->q[2] = q[2]; + b->q[3] = q[3]; + dQtoR (b->q,b->R); +} + +void dBodySetRotation (dBodyID b, const dMatrix3 R) +{ + dAASSERT (b && R); + dQuaternion q; + dRtoQ (R,q); + dNormalize4 (q); + b->q[0] = q[0]; + b->q[1] = q[1]; + b->q[2] = q[2]; + b->q[3] = q[3]; + dQtoR (b->q,b->R); + + // notify all attached geoms that this body has moved + for (dxGeom *geom = b->geom; geom; geom = dGeomGetBodyNext (geom)) + dGeomMoved (geom); +} + + +void dBodySetQuaternion (dBodyID b, const dQuaternion q) +{ + dAASSERT (b && q); + b->q[0] = q[0]; + b->q[1] = q[1]; + b->q[2] = q[2]; + b->q[3] = q[3]; + dNormalize4 (b->q); + dQtoR (b->q,b->R); + + // notify all attached geoms that this body has moved + for (dxGeom *geom = b->geom; geom; geom = dGeomGetBodyNext (geom)) + dGeomMoved (geom); +} + + +void dBodySetLinearVel (dBodyID b, dReal x, dReal y, dReal z) +{ + dAASSERT (b); + b->lvel[0] = x; + b->lvel[1] = y; + b->lvel[2] = z; +} + + +void dBodySetAngularVel (dBodyID b, dReal x, dReal y, dReal z) +{ + dAASSERT (b); + b->avel[0] = x; + b->avel[1] = y; + b->avel[2] = z; +} + + +const dReal * dBodyGetPosition (dBodyID b) +{ + dAASSERT (b); + return b->pos; +} + + +const dReal * dBodyGetRotation (dBodyID b) +{ + dAASSERT (b); + return b->R; +} + + +const dReal * dBodyGetQuaternion (dBodyID b) +{ + dAASSERT (b); + return b->q; +} + + +const dReal * dBodyGetLinearVel (dBodyID b) +{ + dAASSERT (b); + return b->lvel; +} + + +const dReal * dBodyGetAngularVel (dBodyID b) +{ + dAASSERT (b); + return b->avel; +} + + +void dBodySetMass (dBodyID b, const dMass *mass) +{ + dAASSERT (b && mass); + memcpy (&b->mass,mass,sizeof(dMass)); + if (dInvertPDMatrix (b->mass.I,b->invI,3)==0) { + dDEBUGMSG ("inertia must be positive definite"); + dRSetIdentity (b->invI); + } + b->invMass = dRecip(b->mass.mass); +} + + +void dBodyGetMass (dBodyID b, dMass *mass) +{ + dAASSERT (b && mass); + memcpy (mass,&b->mass,sizeof(dMass)); +} + + +void dBodyAddForce (dBodyID b, dReal fx, dReal fy, dReal fz) +{ + dAASSERT (b); + b->facc[0] += fx; + b->facc[1] += fy; + b->facc[2] += fz; +} + + +void dBodyAddTorque (dBodyID b, dReal fx, dReal fy, dReal fz) +{ + dAASSERT (b); + b->tacc[0] += fx; + b->tacc[1] += fy; + b->tacc[2] += fz; +} + + +void dBodyAddRelForce (dBodyID b, dReal fx, dReal fy, dReal fz) +{ + dAASSERT (b); + dVector3 t1,t2; + t1[0] = fx; + t1[1] = fy; + t1[2] = fz; + t1[3] = 0; + dMULTIPLY0_331 (t2,b->R,t1); + b->facc[0] += t2[0]; + b->facc[1] += t2[1]; + b->facc[2] += t2[2]; +} + + +void dBodyAddRelTorque (dBodyID b, dReal fx, dReal fy, dReal fz) +{ + dAASSERT (b); + dVector3 t1,t2; + t1[0] = fx; + t1[1] = fy; + t1[2] = fz; + t1[3] = 0; + dMULTIPLY0_331 (t2,b->R,t1); + b->tacc[0] += t2[0]; + b->tacc[1] += t2[1]; + b->tacc[2] += t2[2]; +} + + +void dBodyAddForceAtPos (dBodyID b, dReal fx, dReal fy, dReal fz, + dReal px, dReal py, dReal pz) +{ + dAASSERT (b); + b->facc[0] += fx; + b->facc[1] += fy; + b->facc[2] += fz; + dVector3 f,q; + f[0] = fx; + f[1] = fy; + f[2] = fz; + q[0] = px - b->pos[0]; + q[1] = py - b->pos[1]; + q[2] = pz - b->pos[2]; + dCROSS (b->tacc,+=,q,f); +} + + +void dBodyAddForceAtRelPos (dBodyID b, dReal fx, dReal fy, dReal fz, + dReal px, dReal py, dReal pz) +{ + dAASSERT (b); + dVector3 prel,f,p; + f[0] = fx; + f[1] = fy; + f[2] = fz; + f[3] = 0; + prel[0] = px; + prel[1] = py; + prel[2] = pz; + prel[3] = 0; + dMULTIPLY0_331 (p,b->R,prel); + b->facc[0] += f[0]; + b->facc[1] += f[1]; + b->facc[2] += f[2]; + dCROSS (b->tacc,+=,p,f); +} + + +void dBodyAddRelForceAtPos (dBodyID b, dReal fx, dReal fy, dReal fz, + dReal px, dReal py, dReal pz) +{ + dAASSERT (b); + dVector3 frel,f; + frel[0] = fx; + frel[1] = fy; + frel[2] = fz; + frel[3] = 0; + dMULTIPLY0_331 (f,b->R,frel); + b->facc[0] += f[0]; + b->facc[1] += f[1]; + b->facc[2] += f[2]; + dVector3 q; + q[0] = px - b->pos[0]; + q[1] = py - b->pos[1]; + q[2] = pz - b->pos[2]; + dCROSS (b->tacc,+=,q,f); +} + + +void dBodyAddRelForceAtRelPos (dBodyID b, dReal fx, dReal fy, dReal fz, + dReal px, dReal py, dReal pz) +{ + dAASSERT (b); + dVector3 frel,prel,f,p; + frel[0] = fx; + frel[1] = fy; + frel[2] = fz; + frel[3] = 0; + prel[0] = px; + prel[1] = py; + prel[2] = pz; + prel[3] = 0; + dMULTIPLY0_331 (f,b->R,frel); + dMULTIPLY0_331 (p,b->R,prel); + b->facc[0] += f[0]; + b->facc[1] += f[1]; + b->facc[2] += f[2]; + dCROSS (b->tacc,+=,p,f); +} + + +const dReal * dBodyGetForce (dBodyID b) +{ + dAASSERT (b); + return b->facc; +} + + +const dReal * dBodyGetTorque (dBodyID b) +{ + dAASSERT (b); + return b->tacc; +} + + +void dBodySetForce (dBodyID b, dReal x, dReal y, dReal z) +{ + dAASSERT (b); + b->facc[0] = x; + b->facc[1] = y; + b->facc[2] = z; +} + + +void dBodySetTorque (dBodyID b, dReal x, dReal y, dReal z) +{ + dAASSERT (b); + b->tacc[0] = x; + b->tacc[1] = y; + b->tacc[2] = z; +} + + +void dBodyGetRelPointPos (dBodyID b, dReal px, dReal py, dReal pz, + dVector3 result) +{ + dAASSERT (b); + dVector3 prel,p; + prel[0] = px; + prel[1] = py; + prel[2] = pz; + prel[3] = 0; + dMULTIPLY0_331 (p,b->R,prel); + result[0] = p[0] + b->pos[0]; + result[1] = p[1] + b->pos[1]; + result[2] = p[2] + b->pos[2]; +} + + +void dBodyGetRelPointVel (dBodyID b, dReal px, dReal py, dReal pz, + dVector3 result) +{ + dAASSERT (b); + dVector3 prel,p; + prel[0] = px; + prel[1] = py; + prel[2] = pz; + prel[3] = 0; + dMULTIPLY0_331 (p,b->R,prel); + result[0] = b->lvel[0]; + result[1] = b->lvel[1]; + result[2] = b->lvel[2]; + dCROSS (result,+=,b->avel,p); +} + + +void dBodyGetPointVel (dBodyID b, dReal px, dReal py, dReal pz, + dVector3 result) +{ + dAASSERT (b); + dVector3 p; + p[0] = px - b->pos[0]; + p[1] = py - b->pos[1]; + p[2] = pz - b->pos[2]; + p[3] = 0; + result[0] = b->lvel[0]; + result[1] = b->lvel[1]; + result[2] = b->lvel[2]; + dCROSS (result,+=,b->avel,p); +} + + +void dBodyGetPosRelPoint (dBodyID b, dReal px, dReal py, dReal pz, + dVector3 result) +{ + dAASSERT (b); + dVector3 prel; + prel[0] = px - b->pos[0]; + prel[1] = py - b->pos[1]; + prel[2] = pz - b->pos[2]; + prel[3] = 0; + dMULTIPLY1_331 (result,b->R,prel); +} + + +void dBodyVectorToWorld (dBodyID b, dReal px, dReal py, dReal pz, + dVector3 result) +{ + dAASSERT (b); + dVector3 p; + p[0] = px; + p[1] = py; + p[2] = pz; + p[3] = 0; + dMULTIPLY0_331 (result,b->R,p); +} + + +void dBodyVectorFromWorld (dBodyID b, dReal px, dReal py, dReal pz, + dVector3 result) +{ + dAASSERT (b); + dVector3 p; + p[0] = px; + p[1] = py; + p[2] = pz; + p[3] = 0; + dMULTIPLY1_331 (result,b->R,p); +} + + +void dBodySetFiniteRotationMode (dBodyID b, int mode) +{ + dAASSERT (b); + b->flags &= ~(dxBodyFlagFiniteRotation | dxBodyFlagFiniteRotationAxis); + if (mode) { + b->flags |= dxBodyFlagFiniteRotation; + if (b->finite_rot_axis[0] != 0 || b->finite_rot_axis[1] != 0 || + b->finite_rot_axis[2] != 0) { + b->flags |= dxBodyFlagFiniteRotationAxis; + } + } +} + + +void dBodySetFiniteRotationAxis (dBodyID b, dReal x, dReal y, dReal z) +{ + dAASSERT (b); + b->finite_rot_axis[0] = x; + b->finite_rot_axis[1] = y; + b->finite_rot_axis[2] = z; + if (x != 0 || y != 0 || z != 0) { + dNormalize3 (b->finite_rot_axis); + b->flags |= dxBodyFlagFiniteRotationAxis; + } + else { + b->flags &= ~dxBodyFlagFiniteRotationAxis; + } +} + + +int dBodyGetFiniteRotationMode (dBodyID b) +{ + dAASSERT (b); + return ((b->flags & dxBodyFlagFiniteRotation) != 0); +} + + +void dBodyGetFiniteRotationAxis (dBodyID b, dVector3 result) +{ + dAASSERT (b); + result[0] = b->finite_rot_axis[0]; + result[1] = b->finite_rot_axis[1]; + result[2] = b->finite_rot_axis[2]; +} + + +int dBodyGetNumJoints (dBodyID b) +{ + dAASSERT (b); + int count=0; + for (dxJointNode *n=b->firstjoint; n; n=n->next, count++); + return count; +} + + +dJointID dBodyGetJoint (dBodyID b, int index) +{ + dAASSERT (b); + int i=0; + for (dxJointNode *n=b->firstjoint; n; n=n->next, i++) { + if (i == index) return n->joint; + } + return 0; +} + + +void dBodyEnable (dBodyID b) +{ + dAASSERT (b); + b->flags &= ~dxBodyDisabled; + b->adis_stepsleft = b->adis.idle_steps; + b->adis_timeleft = b->adis.idle_time; +} + + +void dBodyDisable (dBodyID b) +{ + dAASSERT (b); + b->flags |= dxBodyDisabled; +} + + +int dBodyIsEnabled (dBodyID b) +{ + dAASSERT (b); + return ((b->flags & dxBodyDisabled) == 0); +} + + +void dBodySetGravityMode (dBodyID b, int mode) +{ + dAASSERT (b); + if (mode) b->flags &= ~dxBodyNoGravity; + else b->flags |= dxBodyNoGravity; +} + + +int dBodyGetGravityMode (dBodyID b) +{ + dAASSERT (b); + return ((b->flags & dxBodyNoGravity) == 0); +} + + +// body auto-disable functions + +dReal dBodyGetAutoDisableLinearThreshold (dBodyID b) +{ + dAASSERT(b); + return dSqrt (b->adis.linear_threshold); +} + + +void dBodySetAutoDisableLinearThreshold (dBodyID b, dReal linear_threshold) +{ + dAASSERT(b); + b->adis.linear_threshold = linear_threshold * linear_threshold; +} + + +dReal dBodyGetAutoDisableAngularThreshold (dBodyID b) +{ + dAASSERT(b); + return dSqrt (b->adis.angular_threshold); +} + + +void dBodySetAutoDisableAngularThreshold (dBodyID b, dReal angular_threshold) +{ + dAASSERT(b); + b->adis.angular_threshold = angular_threshold * angular_threshold; +} + + +int dBodyGetAutoDisableSteps (dBodyID b) +{ + dAASSERT(b); + return b->adis.idle_steps; +} + + +void dBodySetAutoDisableSteps (dBodyID b, int steps) +{ + dAASSERT(b); + b->adis.idle_steps = steps; +} + + +dReal dBodyGetAutoDisableTime (dBodyID b) +{ + dAASSERT(b); + return b->adis.idle_time; +} + + +void dBodySetAutoDisableTime (dBodyID b, dReal time) +{ + dAASSERT(b); + b->adis.idle_time = time; +} + + +int dBodyGetAutoDisableFlag (dBodyID b) +{ + dAASSERT(b); + return ((b->flags & dxBodyAutoDisable) != 0); +} + + +void dBodySetAutoDisableFlag (dBodyID b, int do_auto_disable) +{ + dAASSERT(b); + if (!do_auto_disable) b->flags &= ~dxBodyAutoDisable; + else b->flags |= dxBodyAutoDisable; +} + + +void dBodySetAutoDisableDefaults (dBodyID b) +{ + dAASSERT(b); + dWorldID w = b->world; + dAASSERT(w); + b->adis = w->adis; + dBodySetAutoDisableFlag (b, w->adis_flag); +} + +//**************************************************************************** +// joints + +static void dJointInit (dxWorld *w, dxJoint *j) +{ + dIASSERT (w && j); + initObject (j,w); + j->vtable = 0; + j->flags = 0; + j->node[0].joint = j; + j->node[0].body = 0; + j->node[0].next = 0; + j->node[1].joint = j; + j->node[1].body = 0; + j->node[1].next = 0; + j->erp = w->global_erp; // Philip + dSetZero (j->lambda,6); + addObjectToList (j,(dObject **) &w->firstjoint); + w->nj++; +} + + +static dxJoint *createJoint (dWorldID w, dJointGroupID group, + dxJoint::Vtable *vtable) +{ + dIASSERT (w && vtable); + dxJoint *j; + if (group) { + j = (dxJoint*) group->stack.alloc (vtable->size); + group->num++; + } + else j = (dxJoint*) dAlloc (vtable->size); + dJointInit (w,j); + j->vtable = vtable; + if (group) j->flags |= dJOINT_INGROUP; + if (vtable->init) vtable->init (j); + j->feedback = 0; + return j; +} + + +dxJoint * dJointCreateBall (dWorldID w, dJointGroupID group) +{ + dAASSERT (w); + return createJoint (w,group,&__dball_vtable); +} + + +dxJoint * dJointCreateHinge (dWorldID w, dJointGroupID group) +{ + dAASSERT (w); + return createJoint (w,group,&__dhinge_vtable); +} + + +dxJoint * dJointCreateSlider (dWorldID w, dJointGroupID group) +{ + dAASSERT (w); + return createJoint (w,group,&__dslider_vtable); +} + + +dxJoint * dJointCreateContact (dWorldID w, dJointGroupID group, + const dContact *c) +{ + dAASSERT (w && c); + dxJointContact *j = (dxJointContact *) + createJoint (w,group,&__dcontact_vtable); + j->contact = *c; + return j; +} + + +dxJoint * dJointCreateHinge2 (dWorldID w, dJointGroupID group) +{ + dAASSERT (w); + return createJoint (w,group,&__dhinge2_vtable); +} + + +dxJoint * dJointCreateUniversal (dWorldID w, dJointGroupID group) +{ + dAASSERT (w); + return createJoint (w,group,&__duniversal_vtable); +} + + +dxJoint * dJointCreateFixed (dWorldID w, dJointGroupID group) +{ + dAASSERT (w); + return createJoint (w,group,&__dfixed_vtable); +} + + +dxJoint * dJointCreateNull (dWorldID w, dJointGroupID group) +{ + dAASSERT (w); + return createJoint (w,group,&__dnull_vtable); +} + + +dxJoint * dJointCreateAMotor (dWorldID w, dJointGroupID group) +{ + dAASSERT (w); + return createJoint (w,group,&__damotor_vtable); +} + + +void dJointDestroy (dxJoint *j) +{ + dAASSERT (j); + if (j->flags & dJOINT_INGROUP) return; + removeJointReferencesFromAttachedBodies (j); + removeObjectFromList (j); + j->world->nj--; + dFree (j,j->vtable->size); +} + + +dJointGroupID dJointGroupCreate (int max_size) +{ + // not any more ... dUASSERT (max_size > 0,"max size must be > 0"); + dxJointGroup *group = new dxJointGroup; + group->num = 0; + return group; +} + + +void dJointGroupDestroy (dJointGroupID group) +{ + dAASSERT (group); + dJointGroupEmpty (group); + delete group; +} + + +void dJointGroupEmpty (dJointGroupID group) +{ + // the joints in this group are detached starting from the most recently + // added (at the top of the stack). this helps ensure that the various + // linked lists are not traversed too much, as the joints will hopefully + // be at the start of those lists. + // if any group joints have their world pointer set to 0, their world was + // previously destroyed. no special handling is required for these joints. + + dAASSERT (group); + int i; + dxJoint **jlist = (dxJoint**) ALLOCA (group->num * sizeof(dxJoint*)); + dxJoint *j = (dxJoint*) group->stack.rewind(); + for (i=0; i < group->num; i++) { + jlist[i] = j; + j = (dxJoint*) (group->stack.next (j->vtable->size)); + } + for (i=group->num-1; i >= 0; i--) { + if (jlist[i]->world) { + removeJointReferencesFromAttachedBodies (jlist[i]); + removeObjectFromList (jlist[i]); + jlist[i]->world->nj--; + } + } + group->num = 0; + group->stack.freeAll(); +} + + +void dJointAttach (dxJoint *joint, dxBody *body1, dxBody *body2) +{ + // check arguments + dUASSERT (joint,"bad joint argument"); + dUASSERT (body1 == 0 || body1 != body2,"can't have body1==body2"); + dxWorld *world = joint->world; + dUASSERT ( (!body1 || body1->world == world) && + (!body2 || body2->world == world), + "joint and bodies must be in same world"); + + // check if the joint can not be attached to just one body + dUASSERT (!((joint->flags & dJOINT_TWOBODIES) && + ((body1 != 0) ^ (body2 != 0))), + "joint can not be attached to just one body"); + + // remove any existing body attachments + if (joint->node[0].body || joint->node[1].body) { + removeJointReferencesFromAttachedBodies (joint); + } + + // if a body is zero, make sure that it is body2, so 0 --> node[1].body + if (body1==0) { + body1 = body2; + body2 = 0; + joint->flags |= dJOINT_REVERSE; + } + else { + joint->flags &= (~dJOINT_REVERSE); + } + + // attach to new bodies + joint->node[0].body = body1; + joint->node[1].body = body2; + if (body1) { + joint->node[1].next = body1->firstjoint; + body1->firstjoint = &joint->node[1]; + } + else joint->node[1].next = 0; + if (body2) { + joint->node[0].next = body2->firstjoint; + body2->firstjoint = &joint->node[0]; + } + else { + joint->node[0].next = 0; + } +} + + +void dJointSetData (dxJoint *joint, void *data) +{ + dAASSERT (joint); + joint->userdata = data; +} + + +void *dJointGetData (dxJoint *joint) +{ + dAASSERT (joint); + return joint->userdata; +} + + +int dJointGetType (dxJoint *joint) +{ + dAASSERT (joint); + return joint->vtable->typenum; +} + + +dBodyID dJointGetBody (dxJoint *joint, int index) +{ + dAASSERT (joint); + if (index == 0 || index == 1) { + if (joint->flags & dJOINT_REVERSE) return joint->node[1-index].body; + else return joint->node[index].body; + } + else return 0; +} + + +void dJointSetFeedback (dxJoint *joint, dJointFeedback *f) +{ + dAASSERT (joint); + joint->feedback = f; +} + + +dJointFeedback *dJointGetFeedback (dxJoint *joint) +{ + dAASSERT (joint); + return joint->feedback; +} + + +int dAreConnected (dBodyID b1, dBodyID b2) +{ + dAASSERT (b1 && b2); + // look through b1's neighbour list for b2 + for (dxJointNode *n=b1->firstjoint; n; n=n->next) { + if (n->body == b2) return 1; + } + return 0; +} + + +int dAreConnectedExcluding (dBodyID b1, dBodyID b2, int joint_type) +{ + dAASSERT (b1 && b2); + // look through b1's neighbour list for b2 + for (dxJointNode *n=b1->firstjoint; n; n=n->next) { + if (dJointGetType (n->joint) != joint_type && n->body == b2) return 1; + } + return 0; +} + +//**************************************************************************** +// world + +dxWorld * dWorldCreate() +{ + dxWorld *w = new dxWorld; + w->firstbody = 0; + w->firstjoint = 0; + w->nb = 0; + w->nj = 0; + dSetZero (w->gravity,4); + w->global_erp = REAL(0.2); +#if defined(dSINGLE) + w->global_cfm = 1e-5f; +#elif defined(dDOUBLE) + w->global_cfm = 1e-10; +#else + #error dSINGLE or dDOUBLE must be defined +#endif + + w->adis.linear_threshold = REAL(0.001)*REAL(0.001); // (magnitude squared) + w->adis.angular_threshold = REAL(0.001)*REAL(0.001); // (magnitude squared) + w->adis.idle_steps = 10; + w->adis.idle_time = 0; + w->adis_flag = 0; + + w->qs.num_iterations = 20; + w->qs.w = REAL(1.3); + + w->contactp.max_vel = dInfinity; + w->contactp.min_depth = 0; + + return w; +} + + +void dWorldDestroy (dxWorld *w) +{ + // delete all bodies and joints + dAASSERT (w); + dxBody *nextb, *b = w->firstbody; + while (b) { + nextb = (dxBody*) b->next; + delete b; + b = nextb; + } + dxJoint *nextj, *j = w->firstjoint; + while (j) { + nextj = (dxJoint*)j->next; + if (j->flags & dJOINT_INGROUP) { + // the joint is part of a group, so "deactivate" it instead + j->world = 0; + j->node[0].body = 0; + j->node[0].next = 0; + j->node[1].body = 0; + j->node[1].next = 0; + dMessage (0,"warning: destroying world containing grouped joints"); + } + else { + dFree (j,j->vtable->size); + } + j = nextj; + } + delete w; +} + + +void dWorldSetGravity (dWorldID w, dReal x, dReal y, dReal z) +{ + dAASSERT (w); + w->gravity[0] = x; + w->gravity[1] = y; + w->gravity[2] = z; +} + + +void dWorldGetGravity (dWorldID w, dVector3 g) +{ + dAASSERT (w); + g[0] = w->gravity[0]; + g[1] = w->gravity[1]; + g[2] = w->gravity[2]; +} + + +void dWorldSetERP (dWorldID w, dReal erp) +{ + dAASSERT (w); + w->global_erp = erp; +} + + +dReal dWorldGetERP (dWorldID w) +{ + dAASSERT (w); + return w->global_erp; +} + + +void dWorldSetCFM (dWorldID w, dReal cfm) +{ + dAASSERT (w); + w->global_cfm = cfm; +} + + +dReal dWorldGetCFM (dWorldID w) +{ + dAASSERT (w); + return w->global_cfm; +} + + +void dWorldStep (dWorldID w, dReal stepsize) +{ + dUASSERT (w,"bad world argument"); + dUASSERT (stepsize > 0,"stepsize must be > 0"); + dxProcessIslands (w,stepsize,&dInternalStepIsland); +} + + +void dWorldQuickStep (dWorldID w, dReal stepsize) +{ + dUASSERT (w,"bad world argument"); + dUASSERT (stepsize > 0,"stepsize must be > 0"); + dxProcessIslands (w,stepsize,&dxQuickStepper); +} + + +void dWorldImpulseToForce (dWorldID w, dReal stepsize, + dReal ix, dReal iy, dReal iz, + dVector3 force) +{ + dAASSERT (w); + stepsize = dRecip(stepsize); + force[0] = stepsize * ix; + force[1] = stepsize * iy; + force[2] = stepsize * iz; + // @@@ force[3] = 0; +} + + +// world auto-disable functions + +dReal dWorldGetAutoDisableLinearThreshold (dWorldID w) +{ + dAASSERT(w); + return dSqrt (w->adis.linear_threshold); +} + + +void dWorldSetAutoDisableLinearThreshold (dWorldID w, dReal linear_threshold) +{ + dAASSERT(w); + w->adis.linear_threshold = linear_threshold * linear_threshold; +} + + +dReal dWorldGetAutoDisableAngularThreshold (dWorldID w) +{ + dAASSERT(w); + return dSqrt (w->adis.angular_threshold); +} + + +void dWorldSetAutoDisableAngularThreshold (dWorldID w, dReal angular_threshold) +{ + dAASSERT(w); + w->adis.angular_threshold = angular_threshold * angular_threshold; +} + + +int dWorldGetAutoDisableSteps (dWorldID w) +{ + dAASSERT(w); + return w->adis.idle_steps; +} + + +void dWorldSetAutoDisableSteps (dWorldID w, int steps) +{ + dAASSERT(w); + w->adis.idle_steps = steps; +} + + +dReal dWorldGetAutoDisableTime (dWorldID w) +{ + dAASSERT(w); + return w->adis.idle_time; +} + + +void dWorldSetAutoDisableTime (dWorldID w, dReal time) +{ + dAASSERT(w); + w->adis.idle_time = time; +} + + +int dWorldGetAutoDisableFlag (dWorldID w) +{ + dAASSERT(w); + return w->adis_flag; +} + + +void dWorldSetAutoDisableFlag (dWorldID w, int do_auto_disable) +{ + dAASSERT(w); + w->adis_flag = (do_auto_disable != 0); +} + + +void dWorldSetQuickStepNumIterations (dWorldID w, int num) +{ + dAASSERT(w); + w->qs.num_iterations = num; +} + + +int dWorldGetQuickStepNumIterations (dWorldID w) +{ + dAASSERT(w); + return w->qs.num_iterations; +} + + +void dWorldSetQuickStepW (dWorldID w, dReal param) +{ + dAASSERT(w); + w->qs.w = param; +} + + +dReal dWorldGetQuickStepW (dWorldID w) +{ + dAASSERT(w); + return w->qs.w; +} + + +void dWorldSetContactMaxCorrectingVel (dWorldID w, dReal vel) +{ + dAASSERT(w); + w->contactp.max_vel = vel; +} + + +dReal dWorldGetContactMaxCorrectingVel (dWorldID w) +{ + dAASSERT(w); + return w->contactp.max_vel; +} + + +void dWorldSetContactSurfaceLayer (dWorldID w, dReal depth) +{ + dAASSERT(w); + w->contactp.min_depth = depth; +} + + +dReal dWorldGetContactSurfaceLayer (dWorldID w) +{ + dAASSERT(w); + return w->contactp.min_depth; +} + +//**************************************************************************** +// testing + +#define NUM 100 + +#define DO(x) + + +extern "C" void dTestDataStructures() +{ + int i; + DO(printf ("testDynamicsStuff()\n")); + + dBodyID body [NUM]; + int nb = 0; + dJointID joint [NUM]; + int nj = 0; + + for (i=0; i 0.5) { + DO(printf ("creating body\n")); + body[nb] = dBodyCreate (w); + DO(printf ("\t--> %p\n",body[nb])); + nb++; + checkWorld (w); + DO(printf ("%d BODIES, %d JOINTS\n",nb,nj)); + } + if (nj < NUM && nb > 2 && dRandReal() > 0.5) { + dBodyID b1 = body [dRand() % nb]; + dBodyID b2 = body [dRand() % nb]; + if (b1 != b2) { + DO(printf ("creating joint, attaching to %p,%p\n",b1,b2)); + joint[nj] = dJointCreateBall (w,0); + DO(printf ("\t-->%p\n",joint[nj])); + checkWorld (w); + dJointAttach (joint[nj],b1,b2); + nj++; + checkWorld (w); + DO(printf ("%d BODIES, %d JOINTS\n",nb,nj)); + } + } + if (nj > 0 && nb > 2 && dRandReal() > 0.5) { + dBodyID b1 = body [dRand() % nb]; + dBodyID b2 = body [dRand() % nb]; + if (b1 != b2) { + int k = dRand() % nj; + DO(printf ("reattaching joint %p\n",joint[k])); + dJointAttach (joint[k],b1,b2); + checkWorld (w); + DO(printf ("%d BODIES, %d JOINTS\n",nb,nj)); + } + } + if (nb > 0 && dRandReal() > 0.5) { + int k = dRand() % nb; + DO(printf ("destroying body %p\n",body[k])); + dBodyDestroy (body[k]); + checkWorld (w); + for (; k < (NUM-1); k++) body[k] = body[k+1]; + nb--; + DO(printf ("%d BODIES, %d JOINTS\n",nb,nj)); + } + if (nj > 0 && dRandReal() > 0.5) { + int k = dRand() % nj; + DO(printf ("destroying joint %p\n",joint[k])); + dJointDestroy (joint[k]); + checkWorld (w); + for (; k < (NUM-1); k++) joint[k] = joint[k+1]; + nj--; + DO(printf ("%d BODIES, %d JOINTS\n",nb,nj)); + } + } + + /* + printf ("creating world\n"); + dWorldID w = dWorldCreate(); + checkWorld (w); + printf ("creating body\n"); + dBodyID b1 = dBodyCreate (w); + checkWorld (w); + printf ("creating body\n"); + dBodyID b2 = dBodyCreate (w); + checkWorld (w); + printf ("creating joint\n"); + dJointID j = dJointCreateBall (w); + checkWorld (w); + printf ("attaching joint\n"); + dJointAttach (j,b1,b2); + checkWorld (w); + printf ("destroying joint\n"); + dJointDestroy (j); + checkWorld (w); + printf ("destroying body\n"); + dBodyDestroy (b1); + checkWorld (w); + printf ("destroying body\n"); + dBodyDestroy (b2); + checkWorld (w); + printf ("destroying world\n"); + dWorldDestroy (w); + */ +} diff --git a/HenocUniverse/ode/source/odemath.cpp b/HenocUniverse/ode/source/odemath.cpp new file mode 100755 index 0000000..512eb7b --- /dev/null +++ b/HenocUniverse/ode/source/odemath.cpp @@ -0,0 +1,156 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#include +#include + + +// this may be called for vectors `a' with extremely small magnitude, for +// example the result of a cross product on two nearly perpendicular vectors. +// we must be robust to these small vectors. to prevent numerical error, +// first find the component a[i] with the largest magnitude and then scale +// all the components by 1/a[i]. then we can compute the length of `a' and +// scale the components by 1/l. this has been verified to work with vectors +// containing the smallest representable numbers. + +void dNormalize3 (dVector3 a) +{ + dReal a0,a1,a2,aa0,aa1,aa2,l; + dAASSERT (a); + a0 = a[0]; + a1 = a[1]; + a2 = a[2]; + aa0 = dFabs(a0); + aa1 = dFabs(a1); + aa2 = dFabs(a2); + if (aa1 > aa0) { + if (aa2 > aa1) { + goto aa2_largest; + } + else { // aa1 is largest + a0 /= aa1; + a2 /= aa1; + l = dRecipSqrt (a0*a0 + a2*a2 + 1); + a[0] = a0*l; + a[1] = dCopySign(l,a1); + a[2] = a2*l; + } + } + else { + if (aa2 > aa0) { + aa2_largest: // aa2 is largest + a0 /= aa2; + a1 /= aa2; + l = dRecipSqrt (a0*a0 + a1*a1 + 1); + a[0] = a0*l; + a[1] = a1*l; + a[2] = dCopySign(l,a2); + } + else { // aa0 is largest + if (aa0 <= 0) { + // dDEBUGMSG ("vector has zero size"); ... this messace is annoying + a[0] = 1; // if all a's are zero, this is where we'll end up. + a[1] = 0; // return a default unit length vector. + a[2] = 0; + return; + } + a1 /= aa0; + a2 /= aa0; + l = dRecipSqrt (a1*a1 + a2*a2 + 1); + a[0] = dCopySign(l,a0); + a[1] = a1*l; + a[2] = a2*l; + } + } +} + + +/* OLD VERSION */ +/* +void dNormalize3 (dVector3 a) +{ + dASSERT (a); + dReal l = dDOT(a,a); + if (l > 0) { + l = dRecipSqrt(l); + a[0] *= l; + a[1] *= l; + a[2] *= l; + } + else { + a[0] = 1; + a[1] = 0; + a[2] = 0; + } +} +*/ + + +void dNormalize4 (dVector4 a) +{ + dAASSERT (a); + dReal l = dDOT(a,a)+a[3]*a[3]; + if (l > 0) { + l = dRecipSqrt(l); + a[0] *= l; + a[1] *= l; + a[2] *= l; + a[3] *= l; + } + else { + dDEBUGMSG ("vector has zero size"); + a[0] = 1; + a[1] = 0; + a[2] = 0; + a[3] = 0; + } +} + + +void dPlaneSpace (const dVector3 n, dVector3 p, dVector3 q) +{ + dAASSERT (n && p && q); + if (dFabs(n[2]) > M_SQRT1_2) { + // choose p in y-z plane + dReal a = n[1]*n[1] + n[2]*n[2]; + dReal k = dRecipSqrt (a); + p[0] = 0; + p[1] = -n[2]*k; + p[2] = n[1]*k; + // set q = n x p + q[0] = a*k; + q[1] = -n[0]*p[2]; + q[2] = n[0]*p[1]; + } + else { + // choose p in x-y plane + dReal a = n[0]*n[0] + n[1]*n[1]; + dReal k = dRecipSqrt (a); + p[0] = -n[1]*k; + p[1] = n[0]*k; + p[2] = 0; + // set q = n x p + q[0] = -n[2]*p[1]; + q[1] = n[2]*p[0]; + q[2] = a*k; + } +} diff --git a/HenocUniverse/ode/source/quickstep.cpp b/HenocUniverse/ode/source/quickstep.cpp new file mode 100755 index 0000000..b7e87f8 --- /dev/null +++ b/HenocUniverse/ode/source/quickstep.cpp @@ -0,0 +1,671 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001-2003 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#include "objects.h" +#include "joint.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "lcp.h" +#include "util.h" +#include + +#define ALLOCA dALLOCA16 + +typedef const dReal *dRealPtr; +typedef dReal *dRealMutablePtr; +#define dRealArray(name,n) dReal name[n]; +#define dRealAllocaArray(name,n) dReal *name = (dReal*) ALLOCA ((n)*sizeof(dReal)); + +//*************************************************************************** +// configuration + +// for the SOR and CG methods: +// uncomment the following line to use warm starting. this definitely +// help for motor-driven joints. unfortunately it appears to hurt +// with high-friction contacts using the SOR method. use with care + +#define WARM_STARTING 1 + + +// for the SOR method: +// uncomment the following line to determine a new constraint-solving +// order for each iteration. however, the qsort per iteration is expensive, +// and the optimal order is somewhat problem dependent. +// @@@ try the leaf->root ordering. + +//#define REORDER_CONSTRAINTS 1 + + +// for the SOR method: +// uncomment the following line to randomly reorder constraint rows +// during the solution. depending on the situation, this can help a lot +// or hardly at all, but it doesn't seem to hurt. + +#define RANDOMLY_REORDER_CONSTRAINTS 1 + +//*************************************************************************** +// testing stuff + +#ifdef TIMING +#define IFTIMING(x) x +#else +#define IFTIMING(x) /* */ +#endif + +//*************************************************************************** +// various common computations involving the matrix J + +// compute iMJ = inv(M)*J' + +static void compute_invM_JT (int m, dRealMutablePtr J, dRealMutablePtr iMJ, int *jb, + dxBody * const *body, dRealPtr invI) +{ + int i,j; + dRealMutablePtr iMJ_ptr = iMJ; + dRealMutablePtr J_ptr = J; + for (i=0; iinvMass; + for (j=0; j<3; j++) iMJ_ptr[j] = k*J_ptr[j]; // Philip + dMULTIPLY0_331 (iMJ_ptr + 3, invI + 12*b1, J_ptr + 3); + if (b2 >= 0) { + k = body[b2]->invMass; + for (j=0; j<3; j++) iMJ_ptr[j+6] = k*J_ptr[j+6]; // Philip + dMULTIPLY0_331 (iMJ_ptr + 9, invI + 12*b2, J_ptr + 9); + } + J_ptr += 12; + iMJ_ptr += 12; + } +} + + +// compute out = inv(M)*J'*in. + +static void multiply_invM_JT (int m, int nb, dRealMutablePtr iMJ, int *jb, + dRealMutablePtr in, dRealMutablePtr out) +{ + int i,j; + dSetZero (out,6*nb); + dRealPtr iMJ_ptr = iMJ; + for (i=0; i= 0) { + out_ptr = out + b2*6; + for (j=0; j<6; j++) out_ptr[j] += iMJ_ptr[j] * in[i]; + } + iMJ_ptr += 6; + } +} + + +// compute out = J*in. + +static void multiply_J (int m, dRealMutablePtr J, int *jb, + dRealMutablePtr in, dRealMutablePtr out) +{ + int i,j; + dRealPtr J_ptr = J; + for (i=0; i= 0) { + in_ptr = in + b2*6; + for (j=0; j<6; j++) sum += J_ptr[j] * in_ptr[j]; + } + J_ptr += 6; + out[i] = sum; + } +} + + +// compute out = (J*inv(M)*J' + cfm)*in. +// use z as an nb*6 temporary. + +static void multiply_J_invM_JT (int m, int nb, dRealMutablePtr J, dRealMutablePtr iMJ, int *jb, + dRealPtr cfm, dRealMutablePtr z, dRealMutablePtr in, dRealMutablePtr out) +{ + multiply_invM_JT (m,nb,iMJ,jb,in,z); + multiply_J (m,J,jb,z,out); + + // add cfm + for (int i=0; ifindex < 0 && i2->findex >= 0) return -1; + if (i1->findex >= 0 && i2->findex < 0) return 1; + if (i1->error < i2->error) return -1; + if (i1->error > i2->error) return 1; + return 0; +} + +#endif + + +static void SOR_LCP (int m, int nb, dRealMutablePtr J, int *jb, dxBody * const *body, + dRealPtr invI, dRealMutablePtr lambda, dRealMutablePtr fc, dRealMutablePtr b, + dRealMutablePtr lo, dRealMutablePtr hi, dRealPtr cfm, int *findex, + dxQuickStepParameters *qs) +{ + const int num_iterations = qs->num_iterations; + const dReal sor_w = qs->w; // SOR over-relaxation parameter + + int i,j; + +#ifdef WARM_STARTING + // for warm starting, this seems to be necessary to prevent + // jerkiness in motor-driven joints. i have no idea why this works. + for (i=0; i= 0) { + for (j=6; j<12; j++) sum += iMJ_ptr[j] * J_ptr[j]; + } + iMJ_ptr += 12; + J_ptr += 12; + Ad[i] = sor_w / (sum + cfm[i]); + } + + // scale J and b by Ad + J_ptr = J; + for (i=0; i= 0) order[j++].index = i; + dIASSERT (j==m); +#endif + + for (int iteration=0; iteration < num_iterations; iteration++) { + +#ifdef REORDER_CONSTRAINTS + // constraints with findex < 0 always come first. + if (iteration < 2) { + // for the first two iterations, solve the constraints in + // the given order + for (i=0; i v2) ? v1 : v2; + if (max > 0) { + //@@@ relative error: order[i].error = dFabs(lambda[i]-last_lambda[i])/max; + order[i].error = dFabs(lambda[i]-last_lambda[i]); + } + else { + order[i].error = dInfinity; + } + order[i].findex = findex[i]; + order[i].index = i; + } + } + qsort (order,m,sizeof(IndexError),&compare_index_error); +#endif +#ifdef RANDOMLY_REORDER_CONSTRAINTS + if ((iteration & 7) == 0) { + for (i=1; i= 0) { + hi[index] = dFabs (hicopy[index] * lambda[findex[index]]); + lo[index] = -hi[index]; + } + + int b1 = jb[index*2]; + int b2 = jb[index*2+1]; + dReal delta = b[index] - lambda[index]*Ad[index]; + dRealMutablePtr fc_ptr = fc + 6*b1; + + // @@@ potential optimization: SIMD-ize this and the b2 >= 0 case + delta -=fc_ptr[0] * J_ptr[0] + fc_ptr[1] * J_ptr[1] + + fc_ptr[2] * J_ptr[2] + fc_ptr[3] * J_ptr[3] + + fc_ptr[4] * J_ptr[4] + fc_ptr[5] * J_ptr[5]; + // @@@ potential optimization: handle 1-body constraints in a separate + // loop to avoid the cost of test & jump? + if (b2 >= 0) { + fc_ptr = fc + 6*b2; + delta -=fc_ptr[0] * J_ptr[6] + fc_ptr[1] * J_ptr[7] + + fc_ptr[2] * J_ptr[8] + fc_ptr[3] * J_ptr[9] + + fc_ptr[4] * J_ptr[10] + fc_ptr[5] * J_ptr[11]; + } + + // compute lambda and clamp it to [lo,hi]. + // @@@ potential optimization: does SSE have clamping instructions + // to save test+jump penalties here? + dReal new_lambda = lambda[index] + delta; + if (new_lambda < lo[index]) { + delta = lo[index]-lambda[index]; + lambda[index] = lo[index]; + } + else if (new_lambda > hi[index]) { + delta = hi[index]-lambda[index]; + lambda[index] = hi[index]; + } + else { + lambda[index] = new_lambda; + } + + //@@@ a trick that may or may not help + //dReal ramp = (1-((dReal)(iteration+1)/(dReal)num_iterations)); + //delta *= ramp; + + // update fc. + // @@@ potential optimization: SIMD for this and the b2 >= 0 case + fc_ptr = fc + 6*b1; + fc_ptr[0] += delta * iMJ_ptr[0]; + fc_ptr[1] += delta * iMJ_ptr[1]; + fc_ptr[2] += delta * iMJ_ptr[2]; + fc_ptr[3] += delta * iMJ_ptr[3]; + fc_ptr[4] += delta * iMJ_ptr[4]; + fc_ptr[5] += delta * iMJ_ptr[5]; + // @@@ potential optimization: handle 1-body constraints in a separate + // loop to avoid the cost of test & jump? + if (b2 >= 0) { + fc_ptr = fc + 6*b2; + fc_ptr[0] += delta * iMJ_ptr[6]; + fc_ptr[1] += delta * iMJ_ptr[7]; + fc_ptr[2] += delta * iMJ_ptr[8]; + fc_ptr[3] += delta * iMJ_ptr[9]; + fc_ptr[4] += delta * iMJ_ptr[10]; + fc_ptr[5] += delta * iMJ_ptr[11]; + } + } + } +} + + +void dxQuickStepper (dxWorld *world, dxBody * const *body, int nb, + dxJoint * const *_joint, int nj, dReal stepsize) +{ + int i,j; + IFTIMING(dTimerStart("preprocessing");) + + dReal stepsize1 = dRecip(stepsize); + + // number all bodies in the body list - set their tag values + for (i=0; itag = i; + + // make a local copy of the joint array, because we might want to modify it. + // (the "dxJoint *const*" declaration says we're allowed to modify the joints + // but not the joint array, because the caller might need it unchanged). + //@@@ do we really need to do this? we'll be sorting constraint rows individually, not joints + dxJoint **joint = (dxJoint**) alloca (nj * sizeof(dxJoint*)); + memcpy (joint,_joint,nj * sizeof(dxJoint*)); + + // for all bodies, compute the inertia tensor and its inverse in the global + // frame, and compute the rotational force and add it to the torque + // accumulator. I and invI are a vertical stack of 3x4 matrices, one per body. + dRealAllocaArray (I,3*4*nb); // need to remember all I's for feedback purposes only + dRealAllocaArray (invI,3*4*nb); + for (i=0; imass.I,body[i]->R); + dMULTIPLY0_333 (I+i*12,body[i]->R,tmp); + // compute inverse inertia tensor in global frame + dMULTIPLY2_333 (tmp,body[i]->invI,body[i]->R); + dMULTIPLY0_333 (invI+i*12,body[i]->R,tmp); + // compute rotational force + dMULTIPLY0_331 (tmp,I+i*12,body[i]->avel); + dCROSS (body[i]->tacc,-=,body[i]->avel,tmp); + } + + // add the gravity force to all bodies + for (i=0; iflags & dxBodyNoGravity)==0) { + body[i]->facc[0] += body[i]->mass.mass * world->gravity[0]; + body[i]->facc[1] += body[i]->mass.mass * world->gravity[1]; + //body[i]->facc[2] += body[i]->mass.mass * world->gravity[2]; // Philip + body[i]->facc[2] = 0; + } + } + + // get joint information (m = total constraint dimension, nub = number of unbounded variables). + // joints with m=0 are inactive and are removed from the joints array + // entirely, so that the code that follows does not consider them. + //@@@ do we really need to save all the info1's + dxJoint::Info1 *info = (dxJoint::Info1*) alloca (nj*sizeof(dxJoint::Info1)); + for (i=0, j=0; jvtable->getInfo1 (joint[j],info+i); + dIASSERT (info[i].m >= 0 && info[i].m <= 6 && info[i].nub >= 0 && info[i].nub <= info[i].m); + if (info[i].m > 0) { + joint[i] = joint[j]; + i++; + } + } + nj = i; + + // create the row offset array + int m = 0; + int *ofs = (int*) alloca (nj*sizeof(int)); + for (i=0; i 0) { + // create a constraint equation right hand side vector `c', a constraint + // force mixing vector `cfm', and LCP low and high bound vectors, and an + // 'findex' vector. + dRealAllocaArray (c,m); + dRealAllocaArray (cfm,m); + dRealAllocaArray (lo,m); + dRealAllocaArray (hi,m); + int *findex = (int*) alloca (m*sizeof(int)); + dSetZero (c,m); + dSetValue (cfm,m,world->global_cfm); + dSetValue (lo,m,-dInfinity); + dSetValue (hi,m, dInfinity); + for (i=0; iglobal_erp; // Philip + for (i=0; ierp; // Philip + joint[i]->vtable->getInfo2 (joint[i],&Jinfo); + // adjust returned findex values for global index numbering + for (j=0; j= 0) findex[ofs[i] + j] += ofs[i]; + } + } + + // create an array of body numbers for each joint row + int *jb_ptr = jb; + for (i=0; inode[0].body) ? (joint[i]->node[0].body->tag) : -1; + int b2 = (joint[i]->node[1].body) ? (joint[i]->node[1].body->tag) : -1; + for (j=0; jinvMass; + for (j=0; j<3; j++) tmp1[i*6+j] = body[i]->facc[j] * body_invMass + body[i]->lvel[j] * stepsize1; // Philip + dMULTIPLY0_331 (tmp1 + i*6 + 3,invI + i*12,body[i]->tacc); + for (j=0; j<3; j++) tmp1[i*6+3+j] += body[i]->avel[j] * stepsize1; // Philip + } + + // put J*tmp1 into rhs + dRealAllocaArray (rhs,m); + multiply_J (m,J,jb,tmp1,rhs); + + // complete rhs + for (i=0; ilambda,info[i].m * sizeof(dReal)); + } +#endif + + // solve the LCP problem and get lambda and invM*constraint_force + IFTIMING (dTimerNow ("solving LCP problem");) + dRealAllocaArray (cforce,nb*6); + SOR_LCP (m,nb,J,jb,body,invI,lambda,cforce,rhs,lo,hi,cfm,findex,&world->qs); + +#ifdef WARM_STARTING + // save lambda for the next iteration + //@@@ note that this doesn't work for contact joints yet, as they are + // recreated every iteration + for (i=0; ilambda,lambda+ofs[i],info[i].m * sizeof(dReal)); + } +#endif + + // note that the SOR method overwrites rhs and J at this point, so + // they should not be used again. + + // add stepsize * cforce to the body velocity + for (i=0; ilvel[j] += stepsize * cforce[i*6+j]; // Philip + for (j=0; j<3; j++) body[i]->avel[j] += stepsize * cforce[i*6+3+j]; // Philip + } + + // if joint feedback is requested, compute the constraint force. + // BUT: cforce is inv(M)*J'*lambda, whereas we want just J'*lambda, + // so we must compute M*cforce. + // @@@ if any joint has a feedback request we compute the entire + // adjusted cforce, which is not the most efficient way to do it. + for (j=0; jfeedback) { + // compute adjusted cforce + for (i=0; imass.mass; + cforce [i*6+0] *= k; + cforce [i*6+1] *= k; + //cforce [i*6+2] *= k; // Philip + cforce [i*6+2] *= 0; + dVector3 tmp; + dMULTIPLY0_331 (tmp, I + 12*i, cforce + i*6 + 3); + cforce [i*6+3] = tmp[0]; + cforce [i*6+4] = tmp[1]; + //cforce [i*6+5] = tmp[2]; // Philip + cforce [i*6+5] = 0; // Philip + } + // compute feedback for this and all remaining joints + for (; jfeedback; + if (fb) { + int b1 = joint[j]->node[0].body->tag; + memcpy (fb->f1,cforce+b1*6,3*sizeof(dReal)); + memcpy (fb->t1,cforce+b1*6+3,3*sizeof(dReal)); + if (joint[j]->node[1].body) { + int b2 = joint[j]->node[1].body->tag; + memcpy (fb->f2,cforce+b2*6,3*sizeof(dReal)); + memcpy (fb->t2,cforce+b2*6+3,3*sizeof(dReal)); + } + } + } + } + } + } + + // compute the velocity update: + // add stepsize * invM * fe to the body velocity + + IFTIMING (dTimerNow ("compute velocity update");) + for (i=0; iinvMass; + for (j=0; j<3; j++) body[i]->lvel[j] += stepsize * body_invMass * body[i]->facc[j]; // Philip + for (j=0; j<3; j++) body[i]->tacc[j] *= stepsize; // Philip + dMULTIPLYADD0_331 (body[i]->avel,invI + i*12,body[i]->tacc); + } + + // update the position and orientation from the new linear/angular velocity + // (over the given timestep) + IFTIMING (dTimerNow ("update position");) + for (i=0; ifacc,3); // Philip + dSetZero (body[i]->tacc,3); // Philip + } + + IFTIMING (dTimerEnd();) + IFTIMING (if (m > 0) dTimerReport (stdout,1);) +} diff --git a/HenocUniverse/ode/source/quickstep.h b/HenocUniverse/ode/source/quickstep.h new file mode 100755 index 0000000..43863e7 --- /dev/null +++ b/HenocUniverse/ode/source/quickstep.h @@ -0,0 +1,33 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef _ODE_QUICK_STEP_H_ +#define _ODE_QUICK_STEP_H_ + +#include + + +void dxQuickStepper (dxWorld *world, dxBody * const *body, int nb, + dxJoint * const *_joint, int nj, dReal stepsize); + + +#endif diff --git a/HenocUniverse/ode/source/rotation.cpp b/HenocUniverse/ode/source/rotation.cpp new file mode 100755 index 0000000..1180e6d --- /dev/null +++ b/HenocUniverse/ode/source/rotation.cpp @@ -0,0 +1,304 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* + +quaternions have the format: (s,vx,vy,vz) where (vx,vy,vz) is the +"rotation axis" and s is the "rotation angle". + +*/ + +#include +#include + + +#define _R(i,j) R[(i)*4+(j)] + +#define SET_3x3_IDENTITY \ + _R(0,0) = REAL(1.0); \ + _R(0,1) = REAL(0.0); \ + _R(0,2) = REAL(0.0); \ + _R(0,3) = REAL(0.0); \ + _R(1,0) = REAL(0.0); \ + _R(1,1) = REAL(1.0); \ + _R(1,2) = REAL(0.0); \ + _R(1,3) = REAL(0.0); \ + _R(2,0) = REAL(0.0); \ + _R(2,1) = REAL(0.0); \ + _R(2,2) = REAL(1.0); \ + _R(2,3) = REAL(0.0); + + +void dRSetIdentity (dMatrix3 R) +{ + dAASSERT (R); + SET_3x3_IDENTITY; +} + + +void dRFromAxisAndAngle (dMatrix3 R, dReal ax, dReal ay, dReal az, + dReal angle) +{ + dAASSERT (R); + dQuaternion q; + dQFromAxisAndAngle (q,ax,ay,az,angle); + dQtoR (q,R); +} + + +void dRFromEulerAngles (dMatrix3 R, dReal phi, dReal theta, dReal psi) +{ + dReal sphi,cphi,stheta,ctheta,spsi,cpsi; + dAASSERT (R); + sphi = dSin(phi); + cphi = dCos(phi); + stheta = dSin(theta); + ctheta = dCos(theta); + spsi = dSin(psi); + cpsi = dCos(psi); + _R(0,0) = cpsi*ctheta; + _R(0,1) = spsi*ctheta; + _R(0,2) =-stheta; + _R(1,0) = cpsi*stheta*sphi - spsi*cphi; + _R(1,1) = spsi*stheta*sphi + cpsi*cphi; + _R(1,2) = ctheta*sphi; + _R(2,0) = cpsi*stheta*cphi + spsi*sphi; + _R(2,1) = spsi*stheta*cphi - cpsi*sphi; + _R(2,2) = ctheta*cphi; +} + + +void dRFrom2Axes (dMatrix3 R, dReal ax, dReal ay, dReal az, + dReal bx, dReal by, dReal bz) +{ + dReal l,k; + dAASSERT (R); + l = dSqrt (ax*ax + ay*ay + az*az); + if (l <= REAL(0.0)) { + dDEBUGMSG ("zero length vector"); + return; + } + l = dRecip(l); + ax *= l; + ay *= l; + az *= l; + k = ax*bx + ay*by + az*bz; + bx -= k*ax; + by -= k*ay; + bz -= k*az; + l = dSqrt (bx*bx + by*by + bz*bz); + if (l <= REAL(0.0)) { + dDEBUGMSG ("zero length vector"); + return; + } + l = dRecip(l); + bx *= l; + by *= l; + bz *= l; + _R(0,0) = ax; + _R(1,0) = ay; + _R(2,0) = az; + _R(0,1) = bx; + _R(1,1) = by; + _R(2,1) = bz; + _R(0,2) = - by*az + ay*bz; + _R(1,2) = - bz*ax + az*bx; + _R(2,2) = - bx*ay + ax*by; +} + + +void dRFromZAxis (dMatrix3 R, dReal ax, dReal ay, dReal az) +{ + dVector3 n,p,q; + n[0] = ax; + n[1] = ay; + n[2] = az; + dNormalize3 (n); + dPlaneSpace (n,p,q); + _R(0,0) = p[0]; + _R(1,0) = p[1]; + _R(2,0) = p[2]; + _R(0,1) = q[0]; + _R(1,1) = q[1]; + _R(2,1) = q[2]; + _R(0,2) = n[0]; + _R(1,2) = n[1]; + _R(2,2) = n[2]; +} + + +void dQSetIdentity (dQuaternion q) +{ + dAASSERT (q); + q[0] = 1; + q[1] = 0; + q[2] = 0; + q[3] = 0; +} + + +void dQFromAxisAndAngle (dQuaternion q, dReal ax, dReal ay, dReal az, + dReal angle) +{ + dAASSERT (q); + dReal l = ax*ax + ay*ay + az*az; + if (l > REAL(0.0)) { + angle *= REAL(0.5); + q[0] = dCos (angle); + l = dSin(angle) * dRecipSqrt(l); + q[1] = ax*l; + q[2] = ay*l; + q[3] = az*l; + } + else { + q[0] = 1; + q[1] = 0; + q[2] = 0; + q[3] = 0; + } +} + + +void dQMultiply0 (dQuaternion qa, const dQuaternion qb, const dQuaternion qc) +{ + dAASSERT (qa && qb && qc); + qa[0] = qb[0]*qc[0] - qb[1]*qc[1] - qb[2]*qc[2] - qb[3]*qc[3]; + qa[1] = qb[0]*qc[1] + qb[1]*qc[0] + qb[2]*qc[3] - qb[3]*qc[2]; + qa[2] = qb[0]*qc[2] + qb[2]*qc[0] + qb[3]*qc[1] - qb[1]*qc[3]; + qa[3] = qb[0]*qc[3] + qb[3]*qc[0] + qb[1]*qc[2] - qb[2]*qc[1]; +} + + +void dQMultiply1 (dQuaternion qa, const dQuaternion qb, const dQuaternion qc) +{ + dAASSERT (qa && qb && qc); + qa[0] = qb[0]*qc[0] + qb[1]*qc[1] + qb[2]*qc[2] + qb[3]*qc[3]; + qa[1] = qb[0]*qc[1] - qb[1]*qc[0] - qb[2]*qc[3] + qb[3]*qc[2]; + qa[2] = qb[0]*qc[2] - qb[2]*qc[0] - qb[3]*qc[1] + qb[1]*qc[3]; + qa[3] = qb[0]*qc[3] - qb[3]*qc[0] - qb[1]*qc[2] + qb[2]*qc[1]; +} + + +void dQMultiply2 (dQuaternion qa, const dQuaternion qb, const dQuaternion qc) +{ + dAASSERT (qa && qb && qc); + qa[0] = qb[0]*qc[0] + qb[1]*qc[1] + qb[2]*qc[2] + qb[3]*qc[3]; + qa[1] = -qb[0]*qc[1] + qb[1]*qc[0] - qb[2]*qc[3] + qb[3]*qc[2]; + qa[2] = -qb[0]*qc[2] + qb[2]*qc[0] - qb[3]*qc[1] + qb[1]*qc[3]; + qa[3] = -qb[0]*qc[3] + qb[3]*qc[0] - qb[1]*qc[2] + qb[2]*qc[1]; +} + + +void dQMultiply3 (dQuaternion qa, const dQuaternion qb, const dQuaternion qc) +{ + dAASSERT (qa && qb && qc); + qa[0] = qb[0]*qc[0] - qb[1]*qc[1] - qb[2]*qc[2] - qb[3]*qc[3]; + qa[1] = -qb[0]*qc[1] - qb[1]*qc[0] + qb[2]*qc[3] - qb[3]*qc[2]; + qa[2] = -qb[0]*qc[2] - qb[2]*qc[0] + qb[3]*qc[1] - qb[1]*qc[3]; + qa[3] = -qb[0]*qc[3] - qb[3]*qc[0] + qb[1]*qc[2] - qb[2]*qc[1]; +} + + +// dRfromQ(), dQfromR() and dDQfromW() are derived from equations in "An Introduction +// to Physically Based Modeling: Rigid Body Simulation - 1: Unconstrained +// Rigid Body Dynamics" by David Baraff, Robotics Institute, Carnegie Mellon +// University, 1997. + +void dRfromQ (dMatrix3 R, const dQuaternion q) +{ + dAASSERT (q && R); + // q = (s,vx,vy,vz) + dReal qq1 = 2*q[1]*q[1]; + dReal qq2 = 2*q[2]*q[2]; + dReal qq3 = 2*q[3]*q[3]; + _R(0,0) = 1 - qq2 - qq3; + _R(0,1) = 2*(q[1]*q[2] - q[0]*q[3]); + _R(0,2) = 2*(q[1]*q[3] + q[0]*q[2]); + _R(1,0) = 2*(q[1]*q[2] + q[0]*q[3]); + _R(1,1) = 1 - qq1 - qq3; + _R(1,2) = 2*(q[2]*q[3] - q[0]*q[1]); + _R(2,0) = 2*(q[1]*q[3] - q[0]*q[2]); + _R(2,1) = 2*(q[2]*q[3] + q[0]*q[1]); + _R(2,2) = 1 - qq1 - qq2; +} + + +void dQfromR (dQuaternion q, const dMatrix3 R) +{ + dAASSERT (q && R); + dReal tr,s; + tr = _R(0,0) + _R(1,1) + _R(2,2); + if (tr >= 0) { + s = dSqrt (tr + 1); + q[0] = REAL(0.5) * s; + s = REAL(0.5) * dRecip(s); + q[1] = (_R(2,1) - _R(1,2)) * s; + q[2] = (_R(0,2) - _R(2,0)) * s; + q[3] = (_R(1,0) - _R(0,1)) * s; + } + else { + // find the largest diagonal element and jump to the appropriate case + if (_R(1,1) > _R(0,0)) { + if (_R(2,2) > _R(1,1)) goto case_2; + goto case_1; + } + if (_R(2,2) > _R(0,0)) goto case_2; + goto case_0; + + case_0: + s = dSqrt((_R(0,0) - (_R(1,1) + _R(2,2))) + 1); + q[1] = REAL(0.5) * s; + s = REAL(0.5) * dRecip(s); + q[2] = (_R(0,1) + _R(1,0)) * s; + q[3] = (_R(2,0) + _R(0,2)) * s; + q[0] = (_R(2,1) - _R(1,2)) * s; + return; + + case_1: + s = dSqrt((_R(1,1) - (_R(2,2) + _R(0,0))) + 1); + q[2] = REAL(0.5) * s; + s = REAL(0.5) * dRecip(s); + q[3] = (_R(1,2) + _R(2,1)) * s; + q[1] = (_R(0,1) + _R(1,0)) * s; + q[0] = (_R(0,2) - _R(2,0)) * s; + return; + + case_2: + s = dSqrt((_R(2,2) - (_R(0,0) + _R(1,1))) + 1); + q[3] = REAL(0.5) * s; + s = REAL(0.5) * dRecip(s); + q[1] = (_R(2,0) + _R(0,2)) * s; + q[2] = (_R(1,2) + _R(2,1)) * s; + q[0] = (_R(1,0) - _R(0,1)) * s; + return; + } +} + + +void dDQfromW (dReal dq[4], const dVector3 w, const dQuaternion q) +{ + dAASSERT (w && q && dq); + dq[0] = REAL(0.5)*(- w[0]*q[1] - w[1]*q[2] - w[2]*q[3]); + dq[1] = REAL(0.5)*( w[0]*q[0] + w[1]*q[3] - w[2]*q[2]); + dq[2] = REAL(0.5)*(- w[0]*q[3] + w[1]*q[0] + w[2]*q[1]); + dq[3] = REAL(0.5)*( w[0]*q[2] - w[1]*q[1] + w[2]*q[0]); +} diff --git a/HenocUniverse/ode/source/stack.h b/HenocUniverse/ode/source/stack.h new file mode 100755 index 0000000..5afff41 --- /dev/null +++ b/HenocUniverse/ode/source/stack.h @@ -0,0 +1,138 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* this comes from the `reuse' library. copy any changes back to the source. + +these stack allocation functions are a replacement for alloca(), except that +they allocate memory from a separate pool. + +advantages over alloca(): + - consecutive allocations are guaranteed to be contiguous with increasing + address. + - functions can allocate stack memory that is returned to the caller, + in other words pushing and popping stack frames is optional. + +disadvantages compared to alloca(): + - less portable + - slightly slower, although still orders of magnitude faster than malloc(). + - longjmp() and exceptions do not deallocate stack memory (but who cares?). + +just like alloca(): + - using too much stack memory does not fail gracefully, it fails with a + segfault. + +*/ + + +#ifndef _ODE_STACK_H_ +#define _ODE_STACK_H_ + + +#ifdef WIN32 +#include "windows.h" +#endif + + +struct dStack { + char *base; // bottom of the stack + int size; // maximum size of the stack + char *pointer; // current top of the stack + char *frame; // linked list of stack frame ptrs +# ifdef WIN32 // stuff for windows: + int pagesize; // - page size - this is ASSUMED to be a power of 2 + int committed; // - bytes committed in allocated region +#endif + + // initialize the stack. `max_size' is the maximum size that the stack can + // reach. on unix and windows a `virtual' memory block of this size is + // mapped into the address space but does not actually consume physical + // memory until it is referenced - so it is safe to set this to a high value. + + void init (int max_size); + + + // destroy the stack. this unmaps any virtual memory that was allocated. + + void destroy(); + + + // allocate `size' bytes from the stack and return a pointer to the allocated + // memory. `size' must be >= 0. the returned pointer will be aligned to the + // size of a long int. + + char * alloc (int size) + { + char *ret = pointer; + pointer += ((size-1) | (sizeof(long int)-1) )+1; +# ifdef WIN32 + // for windows we need to commit pages as they are required + if ((pointer-base) > committed) { + committed = ((pointer-base-1) | (pagesize-1))+1; // round up to pgsize + VirtualAlloc (base,committed,MEM_COMMIT,PAGE_READWRITE); + } +# endif + return ret; + } + + + // return the address that will be returned by the next call to alloc() + + char *nextAlloc() + { + return pointer; + } + + + // push and pop the current size of the stack. pushFrame() saves the current + // frame pointer on the stack, and popFrame() retrieves it. a typical + // stack-using function will bracket alloc() calls with pushFrame() and + // popFrame(). both functions return the current stack pointer - this should + // be the same value for the two bracketing calls. calling popFrame() too + // many times will result in a segfault. + + char * pushFrame() + { + char *newframe = pointer; + char **addr = (char**) alloc (sizeof(char*)); + *addr = frame; + frame = newframe; + return newframe; + + /* OLD CODE + *((char**)pointer) = frame; + frame = pointer; + char *ret = pointer; + pointer += sizeof(char*); + return ret; + */ + } + + char * popFrame() + { + pointer = frame; + frame = *((char**)pointer); + return pointer; + } +}; + + +#endif diff --git a/HenocUniverse/ode/source/step.cpp b/HenocUniverse/ode/source/step.cpp new file mode 100755 index 0000000..5edc0a3 --- /dev/null +++ b/HenocUniverse/ode/source/step.cpp @@ -0,0 +1,1001 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#include "objects.h" +#include "joint.h" +#include +#include +#include +#include +#include +#include +#include +#include "lcp.h" +#include "util.h" +#include + +//**************************************************************************** +// misc defines + +#define FAST_FACTOR +//#define TIMING + +#define ALLOCA dALLOCA16 + +//**************************************************************************** +// debugging - comparison of various vectors and matrices produced by the +// slow and fast versions of the stepper. + +//#define COMPARE_METHODS + +#ifdef COMPARE_METHODS +#include "testing.h" +dMatrixComparison comparator; +#endif + +//**************************************************************************** +// special matrix multipliers + +// this assumes the 4th and 8th rows of B and C are zero. + +static void Multiply2_p8r (dReal *A, dReal *B, dReal *C, + int p, int r, int Askip) +{ + int i,j; + dReal sum,*bb,*cc; + dIASSERT (p>0 && r>0 && A && B && C); + bb = B; + for (i=p; i; i--) { + cc = C; + for (j=r; j; j--) { + sum = bb[0]*cc[0]; + sum += bb[1]*cc[1]; + sum += bb[2]*cc[2]; + sum += bb[4]*cc[4]; + sum += bb[5]*cc[5]; + sum += bb[6]*cc[6]; + *(A++) = sum; + cc += 8; + } + A += Askip - r; + bb += 8; + } +} + + +// this assumes the 4th and 8th rows of B and C are zero. + +static void MultiplyAdd2_p8r (dReal *A, dReal *B, dReal *C, + int p, int r, int Askip) +{ + int i,j; + dReal sum,*bb,*cc; + dIASSERT (p>0 && r>0 && A && B && C); + bb = B; + for (i=p; i; i--) { + cc = C; + for (j=r; j; j--) { + sum = bb[0]*cc[0]; + sum += bb[1]*cc[1]; + sum += bb[2]*cc[2]; + sum += bb[4]*cc[4]; + sum += bb[5]*cc[5]; + sum += bb[6]*cc[6]; + *(A++) += sum; + cc += 8; + } + A += Askip - r; + bb += 8; + } +} + + +// this assumes the 4th and 8th rows of B are zero. + +static void Multiply0_p81 (dReal *A, dReal *B, dReal *C, int p) +{ + int i; + dIASSERT (p>0 && A && B && C); + dReal sum; + for (i=p; i; i--) { + sum = B[0]*C[0]; + sum += B[1]*C[1]; + sum += B[2]*C[2]; + sum += B[4]*C[4]; + sum += B[5]*C[5]; + sum += B[6]*C[6]; + *(A++) = sum; + B += 8; + } +} + + +// this assumes the 4th and 8th rows of B are zero. + +static void MultiplyAdd0_p81 (dReal *A, dReal *B, dReal *C, int p) +{ + int i; + dIASSERT (p>0 && A && B && C); + dReal sum; + for (i=p; i; i--) { + sum = B[0]*C[0]; + sum += B[1]*C[1]; + sum += B[2]*C[2]; + sum += B[4]*C[4]; + sum += B[5]*C[5]; + sum += B[6]*C[6]; + *(A++) += sum; + B += 8; + } +} + + +// this assumes the 4th and 8th rows of B are zero. + +static void MultiplyAdd1_8q1 (dReal *A, dReal *B, dReal *C, int q) +{ + int k; + dReal sum; + dIASSERT (q>0 && A && B && C); + sum = 0; + for (k=0; k0 && A && B && C); + sum = 0; + for (k=0; ktag = i; + + // make a local copy of the joint array, because we might want to modify it. + // (the "dxJoint *const*" declaration says we're allowed to modify the joints + // but not the joint array, because the caller might need it unchanged). + dxJoint **joint = (dxJoint**) ALLOCA (nj * sizeof(dxJoint*)); + memcpy (joint,_joint,nj * sizeof(dxJoint*)); + + // for all bodies, compute the inertia tensor and its inverse in the global + // frame, and compute the rotational force and add it to the torque + // accumulator. + // @@@ check computation of rotational force. + dReal *I = (dReal*) ALLOCA (3*nb*4 * sizeof(dReal)); + dReal *invI = (dReal*) ALLOCA (3*nb*4 * sizeof(dReal)); + + //dSetZero (I,3*nb*4); + //dSetZero (invI,3*nb*4); + for (i=0; imass.I,body[i]->R); + dMULTIPLY0_333 (I+i*12,body[i]->R,tmp); + // compute inverse inertia tensor in global frame + dMULTIPLY2_333 (tmp,body[i]->invI,body[i]->R); + dMULTIPLY0_333 (invI+i*12,body[i]->R,tmp); + // compute rotational force + dMULTIPLY0_331 (tmp,I+i*12,body[i]->avel); + dCROSS (body[i]->tacc,-=,body[i]->avel,tmp); + } + + // add the gravity force to all bodies + for (i=0; iflags & dxBodyNoGravity)==0) { + body[i]->facc[0] += body[i]->mass.mass * world->gravity[0]; + body[i]->facc[1] += body[i]->mass.mass * world->gravity[1]; + body[i]->facc[2] += body[i]->mass.mass * world->gravity[2]; + } + } + + // get m = total constraint dimension, nub = number of unbounded variables. + // create constraint offset array and number-of-rows array for all joints. + // the constraints are re-ordered as follows: the purely unbounded + // constraints, the mixed unbounded + LCP constraints, and last the purely + // LCP constraints. + // + // joints with m=0 are inactive and are removed from the joints array + // entirely, so that the code that follows does not consider them. + int m = 0; + dxJoint::Info1 *info = (dxJoint::Info1*) ALLOCA (nj*sizeof(dxJoint::Info1)); + int *ofs = (int*) ALLOCA (nj*sizeof(int)); + for (i=0, j=0; jvtable->getInfo1 (joint[j],info+i); + dIASSERT (info[i].m >= 0 && info[i].m <= 6 && + info[i].nub >= 0 && info[i].nub <= info[i].m); + if (info[i].m > 0) { + joint[i] = joint[j]; + i++; + } + } + nj = i; + + // the purely unbounded constraints + for (i=0; i 0 && info[i].nub < info[i].m) { + ofs[i] = m; + m += info[i].m; + } + // the purely LCP constraints + for (i=0; iinvMass; + MM[nskip+1] = body[i]->invMass; + MM[2*nskip+2] = body[i]->invMass; + MM += 3*nskip+3; + for (j=0; j<3; j++) for (k=0; k<3; k++) { + MM[j*nskip+k] = invI[i*12+j*4+k]; + } + } + + // assemble some body vectors: fe = external forces, v = velocities + dReal *fe = (dReal*) ALLOCA (n6 * sizeof(dReal)); + dReal *v = (dReal*) ALLOCA (n6 * sizeof(dReal)); + //dSetZero (fe,n6); + //dSetZero (v,n6); + for (i=0; ifacc[j]; + for (j=0; j<3; j++) fe[i*6+3+j] = body[i]->tacc[j]; + for (j=0; j<3; j++) v[i*6+j] = body[i]->lvel[j]; + for (j=0; j<3; j++) v[i*6+3+j] = body[i]->avel[j]; + } + + // this will be set to the velocity update + dReal *vnew = (dReal*) ALLOCA (n6 * sizeof(dReal)); + dSetZero (vnew,n6); + + // if there are constraints, compute cforce + if (m > 0) { + // create a constraint equation right hand side vector `c', a constraint + // force mixing vector `cfm', and LCP low and high bound vectors, and an + // 'findex' vector. + dReal *c = (dReal*) ALLOCA (m*sizeof(dReal)); + dReal *cfm = (dReal*) ALLOCA (m*sizeof(dReal)); + dReal *lo = (dReal*) ALLOCA (m*sizeof(dReal)); + dReal *hi = (dReal*) ALLOCA (m*sizeof(dReal)); + int *findex = (int*) alloca (m*sizeof(int)); + dSetZero (c,m); + dSetValue (cfm,m,world->global_cfm); + dSetValue (lo,m,-dInfinity); + dSetValue (hi,m, dInfinity); + for (i=0; iglobal_erp; + for (i=0; inode[0].body->tag; + Jinfo.J1a = Jinfo.J1l + 3; + if (joint[i]->node[1].body) { + Jinfo.J2l = J + nskip*ofs[i] + 6*joint[i]->node[1].body->tag; + Jinfo.J2a = Jinfo.J2l + 3; + } + else { + Jinfo.J2l = 0; + Jinfo.J2a = 0; + } + Jinfo.c = c + ofs[i]; + Jinfo.cfm = cfm + ofs[i]; + Jinfo.lo = lo + ofs[i]; + Jinfo.hi = hi + ofs[i]; + Jinfo.findex = findex + ofs[i]; + joint[i]->vtable->getInfo2 (joint[i],&Jinfo); + // adjust returned findex values for global index numbering + for (j=0; j= 0) findex[ofs[i] + j] += ofs[i]; + } + } + + // compute A = J*invM*J' +# ifdef TIMING + dTimerNow ("compute A"); +# endif + dReal *JinvM = (dReal*) ALLOCA (m*nskip*sizeof(dReal)); + //dSetZero (JinvM,m*nskip); + dMultiply0 (JinvM,J,invM,m,n6,n6); + int mskip = dPAD(m); + dReal *A = (dReal*) ALLOCA (m*mskip*sizeof(dReal)); + //dSetZero (A,m*mskip); + dMultiply2 (A,JinvM,J,m,n6,m); + + // add cfm to the diagonal of A + for (i=0; ilvel[j] = vnew[i*6+j]; + for (j=0; j<3; j++) body[i]->avel[j] = vnew[i*6+3+j]; + } + + // update the position and orientation from the new linear/angular velocity + // (over the given timestep) +# ifdef TIMING + dTimerNow ("update position"); +# endif + for (i=0; ifacc[0] = 0; + body[i]->facc[1] = 0; + body[i]->facc[2] = 0; + body[i]->facc[3] = 0; + body[i]->tacc[0] = 0; + body[i]->tacc[1] = 0; + body[i]->tacc[2] = 0; + body[i]->tacc[3] = 0; + } + +# ifdef TIMING + dTimerEnd(); + if (m > 0) dTimerReport (stdout,1); +# endif +} + +//**************************************************************************** +// an optimized version of dInternalStepIsland1() + +void dInternalStepIsland_x2 (dxWorld *world, dxBody * const *body, int nb, + dxJoint * const *_joint, int nj, dReal stepsize) +{ + int i,j,k; +# ifdef TIMING + dTimerStart("preprocessing"); +# endif + + dReal stepsize1 = dRecip(stepsize); + + // number all bodies in the body list - set their tag values + for (i=0; itag = i; + + // make a local copy of the joint array, because we might want to modify it. + // (the "dxJoint *const*" declaration says we're allowed to modify the joints + // but not the joint array, because the caller might need it unchanged). + dxJoint **joint = (dxJoint**) ALLOCA (nj * sizeof(dxJoint*)); + memcpy (joint,_joint,nj * sizeof(dxJoint*)); + + // for all bodies, compute the inertia tensor and its inverse in the global + // frame, and compute the rotational force and add it to the torque + // accumulator. I and invI are vertically stacked 3x4 matrices, one per body. + // @@@ check computation of rotational force. + dReal *I = (dReal*) ALLOCA (3*nb*4 * sizeof(dReal)); + dReal *invI = (dReal*) ALLOCA (3*nb*4 * sizeof(dReal)); + + //dSetZero (I,3*nb*4); + //dSetZero (invI,3*nb*4); + for (i=0; imass.I,body[i]->R); + dMULTIPLY0_333 (I+i*12,body[i]->R,tmp); + // compute inverse inertia tensor in global frame + dMULTIPLY2_333 (tmp,body[i]->invI,body[i]->R); + dMULTIPLY0_333 (invI+i*12,body[i]->R,tmp); + // compute rotational force + dMULTIPLY0_331 (tmp,I+i*12,body[i]->avel); + dCROSS (body[i]->tacc,-=,body[i]->avel,tmp); + } + + // add the gravity force to all bodies + for (i=0; iflags & dxBodyNoGravity)==0) { + body[i]->facc[0] += body[i]->mass.mass * world->gravity[0]; + body[i]->facc[1] += body[i]->mass.mass * world->gravity[1]; + body[i]->facc[2] += body[i]->mass.mass * world->gravity[2]; + } + } + + // get m = total constraint dimension, nub = number of unbounded variables. + // create constraint offset array and number-of-rows array for all joints. + // the constraints are re-ordered as follows: the purely unbounded + // constraints, the mixed unbounded + LCP constraints, and last the purely + // LCP constraints. this assists the LCP solver to put all unbounded + // variables at the start for a quick factorization. + // + // joints with m=0 are inactive and are removed from the joints array + // entirely, so that the code that follows does not consider them. + // also number all active joints in the joint list (set their tag values). + // inactive joints receive a tag value of -1. + + int m = 0; + dxJoint::Info1 *info = (dxJoint::Info1*) ALLOCA (nj*sizeof(dxJoint::Info1)); + int *ofs = (int*) ALLOCA (nj*sizeof(int)); + for (i=0, j=0; jvtable->getInfo1 (joint[j],info+i); + dIASSERT (info[i].m >= 0 && info[i].m <= 6 && + info[i].nub >= 0 && info[i].nub <= info[i].m); + if (info[i].m > 0) { + joint[i] = joint[j]; + joint[i]->tag = i; + i++; + } + else { + joint[j]->tag = -1; + } + } + nj = i; + + // the purely unbounded constraints + for (i=0; i 0 && info[i].nub < info[i].m) { + ofs[i] = m; + m += info[i].m; + } + // the purely LCP constraints + for (i=0; i 0) { + // create a constraint equation right hand side vector `c', a constraint + // force mixing vector `cfm', and LCP low and high bound vectors, and an + // 'findex' vector. + dReal *c = (dReal*) ALLOCA (m*sizeof(dReal)); + dReal *cfm = (dReal*) ALLOCA (m*sizeof(dReal)); + dReal *lo = (dReal*) ALLOCA (m*sizeof(dReal)); + dReal *hi = (dReal*) ALLOCA (m*sizeof(dReal)); + int *findex = (int*) alloca (m*sizeof(int)); + dSetZero (c,m); + dSetValue (cfm,m,world->global_cfm); + dSetValue (lo,m,-dInfinity); + dSetValue (hi,m, dInfinity); + for (i=0; iglobal_erp; + for (i=0; ivtable->getInfo2 (joint[i],&Jinfo); + // adjust returned findex values for global index numbering + for (j=0; j= 0) findex[ofs[i] + j] += ofs[i]; + } + } + + // compute A = J*invM*J'. first compute JinvM = J*invM. this has the same + // format as J so we just go through the constraints in J multiplying by + // the appropriate scalars and matrices. +# ifdef TIMING + dTimerNow ("compute A"); +# endif + dReal *JinvM = (dReal*) ALLOCA (2*m*8*sizeof(dReal)); + dSetZero (JinvM,2*m*8); + for (i=0; inode[0].body->tag; + dReal body_invMass = body[b]->invMass; + dReal *body_invI = invI + b*12; + dReal *Jsrc = J + 2*8*ofs[i]; + dReal *Jdst = JinvM + 2*8*ofs[i]; + for (j=info[i].m-1; j>=0; j--) { + for (k=0; k<3; k++) Jdst[k] = Jsrc[k] * body_invMass; + dMULTIPLY0_133 (Jdst+4,Jsrc+4,body_invI); + Jsrc += 8; + Jdst += 8; + } + if (joint[i]->node[1].body) { + b = joint[i]->node[1].body->tag; + body_invMass = body[b]->invMass; + body_invI = invI + b*12; + for (j=info[i].m-1; j>=0; j--) { + for (k=0; k<3; k++) Jdst[k] = Jsrc[k] * body_invMass; + dMULTIPLY0_133 (Jdst+4,Jsrc+4,body_invI); + Jsrc += 8; + Jdst += 8; + } + } + } + + // now compute A = JinvM * J'. A's rows and columns are grouped by joint, + // i.e. in the same way as the rows of J. block (i,j) of A is only nonzero + // if joints i and j have at least one body in common. this fact suggests + // the algorithm used to fill A: + // + // for b = all bodies + // n = number of joints attached to body b + // for i = 1..n + // for j = i+1..n + // ii = actual joint number for i + // jj = actual joint number for j + // // (ii,jj) will be set to all pairs of joints around body b + // compute blockwise: A(ii,jj) += JinvM(ii) * J(jj)' + // + // this algorithm catches all pairs of joints that have at least one body + // in common. it does not compute the diagonal blocks of A however - + // another similar algorithm does that. + + int mskip = dPAD(m); + dReal *A = (dReal*) ALLOCA (m*mskip*sizeof(dReal)); + dSetZero (A,m*mskip); + for (i=0; ifirstjoint; n1; n1=n1->next) { + for (dxJointNode *n2=n1->next; n2; n2=n2->next) { + // get joint numbers and ensure ofs[j1] >= ofs[j2] + int j1 = n1->joint->tag; + int j2 = n2->joint->tag; + if (ofs[j1] < ofs[j2]) { + int tmp = j1; + j1 = j2; + j2 = tmp; + } + + // if either joint was tagged as -1 then it is an inactive (m=0) + // joint that should not be considered + if (j1==-1 || j2==-1) continue; + + // determine if body i is the 1st or 2nd body of joints j1 and j2 + int jb1 = (joint[j1]->node[1].body == body[i]); + int jb2 = (joint[j2]->node[1].body == body[i]); + // jb1/jb2 must be 0 for joints with only one body + dIASSERT(joint[j1]->node[1].body || jb1==0); + dIASSERT(joint[j2]->node[1].body || jb2==0); + + // set block of A + MultiplyAdd2_p8r (A + ofs[j1]*mskip + ofs[j2], + JinvM + 2*8*ofs[j1] + jb1*8*info[j1].m, + J + 2*8*ofs[j2] + jb2*8*info[j2].m, + info[j1].m,info[j2].m, mskip); + } + } + } + // compute diagonal blocks of A + for (i=0; inode[1].body) { + MultiplyAdd2_p8r (A + ofs[i]*(mskip+1), + JinvM + 2*8*ofs[i] + 8*info[i].m, + J + 2*8*ofs[i] + 8*info[i].m, + info[i].m,info[i].m, mskip); + } + } + + // add cfm to the diagonal of A + for (i=0; iinvMass; + dReal *body_invI = invI + i*12; + for (j=0; j<3; j++) tmp1[i*8+j] = body[i]->facc[j] * body_invMass + + body[i]->lvel[j] * stepsize1; + dMULTIPLY0_331 (tmp1 + i*8 + 4,body_invI,body[i]->tacc); + for (j=0; j<3; j++) tmp1[i*8+4+j] += body[i]->avel[j] * stepsize1; + } + // put J*tmp1 into rhs + dReal *rhs = (dReal*) ALLOCA (m * sizeof(dReal)); + //dSetZero (rhs,m); + for (i=0; inode[0].body->tag, info[i].m); + if (joint[i]->node[1].body) { + MultiplyAdd0_p81 (rhs+ofs[i],JJ + 8*info[i].m, + tmp1 + 8*joint[i]->node[1].body->tag, info[i].m); + } + } + // complete rhs + for (i=0; inode[0].body; + dxBody* b2 = joint[i]->node[1].body; + dJointFeedback *fb = joint[i]->feedback; + + if (fb) { + // the user has requested feedback on the amount of force that this + // joint is applying to the bodies. we use a slightly slower + // computation that splits out the force components and puts them + // in the feedback structure. + dReal data1[8],data2[8]; + Multiply1_8q1 (data1, JJ, lambda+ofs[i], info[i].m); + dReal *cf1 = cforce + 8*b1->tag; + cf1[0] += (fb->f1[0] = data1[0]); + cf1[1] += (fb->f1[1] = data1[1]); + cf1[2] += (fb->f1[2] = data1[2]); + cf1[4] += (fb->t1[0] = data1[4]); + cf1[5] += (fb->t1[1] = data1[5]); + cf1[6] += (fb->t1[2] = data1[6]); + if (b2){ + Multiply1_8q1 (data2, JJ + 8*info[i].m, lambda+ofs[i], info[i].m); + dReal *cf2 = cforce + 8*b2->tag; + cf2[0] += (fb->f2[0] = data2[0]); + cf2[1] += (fb->f2[1] = data2[1]); + cf2[2] += (fb->f2[2] = data2[2]); + cf2[4] += (fb->t2[0] = data2[4]); + cf2[5] += (fb->t2[1] = data2[5]); + cf2[6] += (fb->t2[2] = data2[6]); + } + } + else { + // no feedback is required, let's compute cforce the faster way + MultiplyAdd1_8q1 (cforce + 8*b1->tag,JJ, lambda+ofs[i], info[i].m); + if (b2) { + MultiplyAdd1_8q1 (cforce + 8*b2->tag, + JJ + 8*info[i].m, lambda+ofs[i], info[i].m); + } + } + } + } + + // compute the velocity update +# ifdef TIMING + dTimerNow ("compute velocity update"); +# endif + + // add fe to cforce + for (i=0; ifacc[j]; + for (j=0; j<3; j++) cforce[i*8+4+j] += body[i]->tacc[j]; + } + // multiply cforce by stepsize + for (i=0; i < nb*8; i++) cforce[i] *= stepsize; + // add invM * cforce to the body velocity + for (i=0; iinvMass; + dReal *body_invI = invI + i*12; + for (j=0; j<3; j++) body[i]->lvel[j] += body_invMass * cforce[i*8+j]; + dMULTIPLYADD0_331 (body[i]->avel,body_invI,cforce+i*8+4); + } + + // update the position and orientation from the new linear/angular velocity + // (over the given timestep) +# ifdef TIMING + dTimerNow ("update position"); +# endif + for (i=0; ilvel[j]; + for (j=0; j<3; j++) tmp_vnew[i*6+3+j] = body[i]->avel[j]; + } + comparator.nextMatrix (tmp_vnew,nb*6,1,0,"vnew"); +# endif + +# ifdef TIMING + dTimerNow ("tidy up"); +# endif + + // zero all force accumulators + for (i=0; ifacc[0] = 0; + body[i]->facc[1] = 0; + body[i]->facc[2] = 0; + body[i]->facc[3] = 0; + body[i]->tacc[0] = 0; + body[i]->tacc[1] = 0; + body[i]->tacc[2] = 0; + body[i]->tacc[3] = 0; + } + +# ifdef TIMING + dTimerEnd(); + if (m > 0) dTimerReport (stdout,1); +# endif +} + +//**************************************************************************** + +void dInternalStepIsland (dxWorld *world, dxBody * const *body, int nb, + dxJoint * const *joint, int nj, dReal stepsize) +{ +# ifndef COMPARE_METHODS + dInternalStepIsland_x2 (world,body,nb,joint,nj,stepsize); +# endif + +# ifdef COMPARE_METHODS + int i; + + // save body state + dxBody *state = (dxBody*) ALLOCA (nb*sizeof(dxBody)); + for (i=0; i + + +void dInternalStepIsland (dxWorld *world, + dxBody * const *body, int nb, + dxJoint * const *joint, int nj, + dReal stepsize); + + + +#endif diff --git a/HenocUniverse/ode/source/stepfast.cpp b/HenocUniverse/ode/source/stepfast.cpp new file mode 100755 index 0000000..eeb371b --- /dev/null +++ b/HenocUniverse/ode/source/stepfast.cpp @@ -0,0 +1,1138 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * Fast iterative solver, David Whittaker. Email: david@csworkbench.com * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +// This is the StepFast code by David Whittaker. This code is faster, but +// sometimes less stable than, the original "big matrix" code. +// Refer to the user's manual for more information. +// Note that this source file duplicates a lot of stuff from step.cpp, +// eventually we should move the common code to a third file. + +#include "objects.h" +#include "joint.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lcp.h" +#include "step.h" +#include "util.h" +#include + + +// misc defines + +#define ALLOCA dALLOCA16 + +#define RANDOM_JOINT_ORDER +//#define FAST_FACTOR //use a factorization approximation to the LCP solver (fast, theoretically less accurate) +#define SLOW_LCP //use the old LCP solver +//#define NO_ISLANDS //does not perform island creation code (3~4% of simulation time), body disabling doesn't work +//#define TIMING + + +static int autoEnableDepth = 2; + +void dWorldSetAutoEnableDepthSF1 (dxWorld *world, int autodepth) +{ + if (autodepth > 0) + autoEnableDepth = autodepth; + else + autoEnableDepth = 0; +} + +int dWorldGetAutoEnableDepthSF1 (dxWorld *world) +{ + return autoEnableDepth; +} + +//little bit of math.... the _sym_ functions assume the return matrix will be symmetric +static void +Multiply2_sym_p8p (dReal * A, dReal * B, dReal * C, int p, int Askip) +{ + int i, j; + dReal sum, *aa, *ad, *bb, *cc; + dIASSERT (p > 0 && A && B && C); + bb = B; + for (i = 0; i < p; i++) + { + //aa is going accross the matrix, ad down + aa = ad = A; + cc = C; + for (j = i; j < p; j++) + { + sum = bb[0] * cc[0]; + sum += bb[1] * cc[1]; + sum += bb[2] * cc[2]; + sum += bb[4] * cc[4]; + sum += bb[5] * cc[5]; + sum += bb[6] * cc[6]; + *(aa++) = *ad = sum; + ad += Askip; + cc += 8; + } + bb += 8; + A += Askip + 1; + C += 8; + } +} + +static void +MultiplyAdd2_sym_p8p (dReal * A, dReal * B, dReal * C, int p, int Askip) +{ + int i, j; + dReal sum, *aa, *ad, *bb, *cc; + dIASSERT (p > 0 && A && B && C); + bb = B; + for (i = 0; i < p; i++) + { + //aa is going accross the matrix, ad down + aa = ad = A; + cc = C; + for (j = i; j < p; j++) + { + sum = bb[0] * cc[0]; + sum += bb[1] * cc[1]; + sum += bb[2] * cc[2]; + sum += bb[4] * cc[4]; + sum += bb[5] * cc[5]; + sum += bb[6] * cc[6]; + *(aa++) += sum; + *ad += sum; + ad += Askip; + cc += 8; + } + bb += 8; + A += Askip + 1; + C += 8; + } +} + + +// this assumes the 4th and 8th rows of B are zero. + +static void +Multiply0_p81 (dReal * A, dReal * B, dReal * C, int p) +{ + int i; + dIASSERT (p > 0 && A && B && C); + dReal sum; + for (i = p; i; i--) + { + sum = B[0] * C[0]; + sum += B[1] * C[1]; + sum += B[2] * C[2]; + sum += B[4] * C[4]; + sum += B[5] * C[5]; + sum += B[6] * C[6]; + *(A++) = sum; + B += 8; + } +} + + +// this assumes the 4th and 8th rows of B are zero. + +static void +MultiplyAdd0_p81 (dReal * A, dReal * B, dReal * C, int p) +{ + int i; + dIASSERT (p > 0 && A && B && C); + dReal sum; + for (i = p; i; i--) + { + sum = B[0] * C[0]; + sum += B[1] * C[1]; + sum += B[2] * C[2]; + sum += B[4] * C[4]; + sum += B[5] * C[5]; + sum += B[6] * C[6]; + *(A++) += sum; + B += 8; + } +} + + +// this assumes the 4th and 8th rows of B are zero. + +static void +Multiply1_8q1 (dReal * A, dReal * B, dReal * C, int q) +{ + int k; + dReal sum; + dIASSERT (q > 0 && A && B && C); + sum = 0; + for (k = 0; k < q; k++) + sum += B[k * 8] * C[k]; + A[0] = sum; + sum = 0; + for (k = 0; k < q; k++) + sum += B[1 + k * 8] * C[k]; + A[1] = sum; + sum = 0; + for (k = 0; k < q; k++) + sum += B[2 + k * 8] * C[k]; + A[2] = sum; + sum = 0; + for (k = 0; k < q; k++) + sum += B[4 + k * 8] * C[k]; + A[4] = sum; + sum = 0; + for (k = 0; k < q; k++) + sum += B[5 + k * 8] * C[k]; + A[5] = sum; + sum = 0; + for (k = 0; k < q; k++) + sum += B[6 + k * 8] * C[k]; + A[6] = sum; +} + +//**************************************************************************** +// body rotation + +// return sin(x)/x. this has a singularity at 0 so special handling is needed +// for small arguments. + +static inline dReal +sinc (dReal x) +{ + // if |x| < 1e-4 then use a taylor series expansion. this two term expansion + // is actually accurate to one LS bit within this range if double precision + // is being used - so don't worry! + if (dFabs (x) < 1.0e-4) + return REAL (1.0) - x * x * REAL (0.166666666666666666667); + else + return dSin (x) / x; +} + + +// given a body b, apply its linear and angular rotation over the time +// interval h, thereby adjusting its position and orientation. + +static inline void +moveAndRotateBody (dxBody * b, dReal h) +{ + int j; + + // handle linear velocity + for (j = 0; j < 3; j++) + b->pos[j] += h * b->lvel[j]; + + if (b->flags & dxBodyFlagFiniteRotation) + { + dVector3 irv; // infitesimal rotation vector + dQuaternion q; // quaternion for finite rotation + + if (b->flags & dxBodyFlagFiniteRotationAxis) + { + // split the angular velocity vector into a component along the finite + // rotation axis, and a component orthogonal to it. + dVector3 frv, irv; // finite rotation vector + dReal k = dDOT (b->finite_rot_axis, b->avel); + frv[0] = b->finite_rot_axis[0] * k; + frv[1] = b->finite_rot_axis[1] * k; + frv[2] = b->finite_rot_axis[2] * k; + irv[0] = b->avel[0] - frv[0]; + irv[1] = b->avel[1] - frv[1]; + irv[2] = b->avel[2] - frv[2]; + + // make a rotation quaternion q that corresponds to frv * h. + // compare this with the full-finite-rotation case below. + h *= REAL (0.5); + dReal theta = k * h; + q[0] = dCos (theta); + dReal s = sinc (theta) * h; + q[1] = frv[0] * s; + q[2] = frv[1] * s; + q[3] = frv[2] * s; + } + else + { + // make a rotation quaternion q that corresponds to w * h + dReal wlen = dSqrt (b->avel[0] * b->avel[0] + b->avel[1] * b->avel[1] + b->avel[2] * b->avel[2]); + h *= REAL (0.5); + dReal theta = wlen * h; + q[0] = dCos (theta); + dReal s = sinc (theta) * h; + q[1] = b->avel[0] * s; + q[2] = b->avel[1] * s; + q[3] = b->avel[2] * s; + } + + // do the finite rotation + dQuaternion q2; + dQMultiply0 (q2, q, b->q); + for (j = 0; j < 4; j++) + b->q[j] = q2[j]; + + // do the infitesimal rotation if required + if (b->flags & dxBodyFlagFiniteRotationAxis) + { + dReal dq[4]; + dWtoDQ (irv, b->q, dq); + for (j = 0; j < 4; j++) + b->q[j] += h * dq[j]; + } + } + else + { + // the normal way - do an infitesimal rotation + dReal dq[4]; + dWtoDQ (b->avel, b->q, dq); + for (j = 0; j < 4; j++) + b->q[j] += h * dq[j]; + } + + // normalize the quaternion and convert it to a rotation matrix + dNormalize4 (b->q); + dQtoR (b->q, b->R); + + // notify all attached geoms that this body has moved + for (dxGeom * geom = b->geom; geom; geom = dGeomGetBodyNext (geom)) + dGeomMoved (geom); +} + +//**************************************************************************** +//This is an implementation of the iterated/relaxation algorithm. +//Here is a quick overview of the algorithm per Sergi Valverde's posts to the +//mailing list: +// +// for i=0..N-1 do +// for c = 0..C-1 do +// Solve constraint c-th +// Apply forces to constraint bodies +// next +// next +// Integrate bodies + +void +dInternalStepFast (dxWorld * world, dxBody * body[2], dReal * GI[2], dReal * GinvI[2], dxJoint * joint, dxJoint::Info1 info, dxJoint::Info2 Jinfo, dReal stepsize) +{ + int i, j, k; +# ifdef TIMING + dTimerNow ("constraint preprocessing"); +# endif + + dReal stepsize1 = dRecip (stepsize); + + int m = info.m; + // nothing to do if no constraints. + if (m <= 0) + return; + + int nub = 0; + if (info.nub == info.m) + nub = m; + + // compute A = J*invM*J'. first compute JinvM = J*invM. this has the same + // format as J so we just go through the constraints in J multiplying by + // the appropriate scalars and matrices. +# ifdef TIMING + dTimerNow ("compute A"); +# endif + dReal JinvM[2 * 6 * 8]; + //dSetZero (JinvM, 2 * m * 8); + + dReal *Jsrc = Jinfo.J1l; + dReal *Jdst = JinvM; + if (body[0]) + { + for (j = m - 1; j >= 0; j--) + { + for (k = 0; k < 3; k++) + Jdst[k] = Jsrc[k] * body[0]->invMass; + dMULTIPLY0_133 (Jdst + 4, Jsrc + 4, GinvI[0]); + Jsrc += 8; + Jdst += 8; + } + } + if (body[1]) + { + Jsrc = Jinfo.J2l; + Jdst = JinvM + 8 * m; + for (j = m - 1; j >= 0; j--) + { + for (k = 0; k < 3; k++) + Jdst[k] = Jsrc[k] * body[1]->invMass; + dMULTIPLY0_133 (Jdst + 4, Jsrc + 4, GinvI[1]); + Jsrc += 8; + Jdst += 8; + } + } + + + // now compute A = JinvM * J'. + int mskip = dPAD (m); + dReal A[6 * 8]; + //dSetZero (A, 6 * 8); + + if (body[0]) { + Multiply2_sym_p8p (A, JinvM, Jinfo.J1l, m, mskip); + if (body[1]) + MultiplyAdd2_sym_p8p (A, JinvM + 8 * m, Jinfo.J2l, + m, mskip); + } else { + if (body[1]) + Multiply2_sym_p8p (A, JinvM + 8 * m, Jinfo.J2l, + m, mskip); + } + + // add cfm to the diagonal of A + for (i = 0; i < m; i++) + A[i * mskip + i] += Jinfo.cfm[i] * stepsize1; + + // compute the right hand side `rhs' +# ifdef TIMING + dTimerNow ("compute rhs"); +# endif + dReal tmp1[16]; + //dSetZero (tmp1, 16); + // put v/h + invM*fe into tmp1 + for (i = 0; i < 2; i++) + { + if (!body[i]) + continue; + for (j = 0; j < 3; j++) + tmp1[i * 8 + j] = body[i]->facc[j] * body[i]->invMass + body[i]->lvel[j] * stepsize1; + dMULTIPLY0_331 (tmp1 + i * 8 + 4, GinvI[i], body[i]->tacc); + for (j = 0; j < 3; j++) + tmp1[i * 8 + 4 + j] += body[i]->avel[j] * stepsize1; + } + // put J*tmp1 into rhs + dReal rhs[6]; + //dSetZero (rhs, 6); + + if (body[0]) { + Multiply0_p81 (rhs, Jinfo.J1l, tmp1, m); + if (body[1]) + MultiplyAdd0_p81 (rhs, Jinfo.J2l, tmp1 + 8, m); + } else { + if (body[1]) + Multiply0_p81 (rhs, Jinfo.J2l, tmp1 + 8, m); + } + + // complete rhs + for (i = 0; i < m; i++) + rhs[i] = Jinfo.c[i] * stepsize1 - rhs[i]; + +#ifdef SLOW_LCP + // solve the LCP problem and get lambda. + // this will destroy A but that's okay +# ifdef TIMING + dTimerNow ("solving LCP problem"); +# endif + dReal *lambda = (dReal *) ALLOCA (m * sizeof (dReal)); + dReal *residual = (dReal *) ALLOCA (m * sizeof (dReal)); + dReal lo[6], hi[6]; + memcpy (lo, Jinfo.lo, m * sizeof (dReal)); + memcpy (hi, Jinfo.hi, m * sizeof (dReal)); + dSolveLCP (m, A, lambda, rhs, residual, nub, lo, hi, Jinfo.findex); +#endif + + // LCP Solver replacement: + // This algorithm goes like this: + // Do a straightforward LDLT factorization of the matrix A, solving for + // A*x = rhs + // For each x[i] that is outside of the bounds of lo[i] and hi[i], + // clamp x[i] into that range. + // Substitute into A the now known x's + // subtract the residual away from the rhs. + // Remove row and column i from L, updating the factorization + // place the known x's at the end of the array, keeping up with location in p + // Repeat until all constraints have been clamped or all are within bounds + // + // This is probably only faster in the single joint case where only one repeat is + // the norm. + +#ifdef FAST_FACTOR + // factorize A (L*D*L'=A) +# ifdef TIMING + dTimerNow ("factorize A"); +# endif + dReal d[6]; + dReal L[6 * 8]; + memcpy (L, A, m * mskip * sizeof (dReal)); + dFactorLDLT (L, d, m, mskip); + + // compute lambda +# ifdef TIMING + dTimerNow ("compute lambda"); +# endif + + int left = m; //constraints left to solve. + int remove[6]; + dReal lambda[6]; + dReal x[6]; + int p[6]; + for (i = 0; i < 6; i++) + p[i] = i; + while (true) + { + memcpy (x, rhs, left * sizeof (dReal)); + dSolveLDLT (L, d, x, left, mskip); + + int fixed = 0; + for (i = 0; i < left; i++) + { + j = p[i]; + remove[i] = false; + // This isn't the exact same use of findex as dSolveLCP.... since x[findex] + // may change after I've already clamped x[i], but it should be close + if (Jinfo.findex[j] > -1) + { + dReal f = fabs (Jinfo.hi[j] * x[p[Jinfo.findex[j]]]); + if (x[i] > f) + x[i] = f; + else if (x[i] < -f) + x[i] = -f; + else + continue; + } + else + { + if (x[i] > Jinfo.hi[j]) + x[i] = Jinfo.hi[j]; + else if (x[i] < Jinfo.lo[j]) + x[i] = Jinfo.lo[j]; + else + continue; + } + remove[i] = true; + fixed++; + } + if (fixed == 0 || fixed == left) //no change or all constraints solved + break; + + for (i = 0; i < left; i++) //sub in to right hand side. + if (remove[i]) + for (j = 0; j < left; j++) + if (!remove[j]) + rhs[j] -= A[j * mskip + i] * x[i]; + + for (int r = left - 1; r >= 0; r--) //eliminate row/col for fixed variables + { + if (remove[r]) + { + //dRemoveLDLT adapted for use without row pointers. + if (r == left - 1) + { + left--; + continue; // deleting last row/col is easy + } + else if (r == 0) + { + dReal a[6]; + for (i = 0; i < left; i++) + a[i] = -A[i * mskip]; + a[0] += REAL (1.0); + dLDLTAddTL (L, d, a, left, mskip); + } + else + { + dReal t[6]; + dReal a[6]; + for (i = 0; i < r; i++) + t[i] = L[r * mskip + i] / d[i]; + for (i = 0; i < left - r; i++) + a[i] = dDot (L + (r + i) * mskip, t, r) - A[(r + i) * mskip + r]; + a[0] += REAL (1.0); + dLDLTAddTL (L + r * mskip + r, d + r, a, left - r, mskip); + } + + dRemoveRowCol (L, left, mskip, r); + //end dRemoveLDLT + + left--; + if (r < (left - 1)) + { + dReal tx = x[r]; + memmove (d + r, d + r + 1, (left - r) * sizeof (dReal)); + memmove (rhs + r, rhs + r + 1, (left - r) * sizeof (dReal)); + //x will get written over by rhs anyway, no need to move it around + //just store the fixed value we just discovered in it. + x[left] = tx; + for (i = 0; i < m; i++) + if (p[i] > r && p[i] <= left) + p[i]--; + p[r] = left; + } + } + } + } + + for (i = 0; i < m; i++) + lambda[i] = x[p[i]]; +# endif + // compute the constraint force `cforce' +# ifdef TIMING + dTimerNow ("compute constraint force"); +#endif + + // compute cforce = J'*lambda + dJointFeedback *fb = joint->feedback; + dReal cforce[16]; + //dSetZero (cforce, 16); + + if (fb) + { + // the user has requested feedback on the amount of force that this + // joint is applying to the bodies. we use a slightly slower + // computation that splits out the force components and puts them + // in the feedback structure. + dReal data1[8], data2[8]; + if (body[0]) + { + Multiply1_8q1 (data1, Jinfo.J1l, lambda, m); + dReal *cf1 = cforce; + cf1[0] = (fb->f1[0] = data1[0]); + cf1[1] = (fb->f1[1] = data1[1]); + cf1[2] = (fb->f1[2] = data1[2]); + cf1[4] = (fb->t1[0] = data1[4]); + cf1[5] = (fb->t1[1] = data1[5]); + cf1[6] = (fb->t1[2] = data1[6]); + } + if (body[1]) + { + Multiply1_8q1 (data2, Jinfo.J2l, lambda, m); + dReal *cf2 = cforce + 8; + cf2[0] = (fb->f2[0] = data2[0]); + cf2[1] = (fb->f2[1] = data2[1]); + cf2[2] = (fb->f2[2] = data2[2]); + cf2[4] = (fb->t2[0] = data2[4]); + cf2[5] = (fb->t2[1] = data2[5]); + cf2[6] = (fb->t2[2] = data2[6]); + } + } + else + { + // no feedback is required, let's compute cforce the faster way + if (body[0]) + Multiply1_8q1 (cforce, Jinfo.J1l, lambda, m); + if (body[1]) + Multiply1_8q1 (cforce + 8, Jinfo.J2l, lambda, m); + } + + for (i = 0; i < 2; i++) + { + if (!body[i]) + continue; + for (j = 0; j < 3; j++) + { + body[i]->facc[j] += cforce[i * 8 + j]; + body[i]->tacc[j] += cforce[i * 8 + 4 + j]; + } + } +} + +void +dInternalStepIslandFast (dxWorld * world, dxBody * const *bodies, int nb, dxJoint * const *_joints, int nj, dReal stepsize, int maxiterations) +{ +# ifdef TIMING + dTimerNow ("preprocessing"); +# endif + dxBody *bodyPair[2], *body; + dReal *GIPair[2], *GinvIPair[2]; + dxJoint *joint; + int iter, b, j, i; + dReal ministep = stepsize / maxiterations; + + // make a local copy of the joint array, because we might want to modify it. + // (the "dxJoint *const*" declaration says we're allowed to modify the joints + // but not the joint array, because the caller might need it unchanged). + dxJoint **joints = (dxJoint **) ALLOCA (nj * sizeof (dxJoint *)); + memcpy (joints, _joints, nj * sizeof (dxJoint *)); + + // get m = total constraint dimension, nub = number of unbounded variables. + // create constraint offset array and number-of-rows array for all joints. + // the constraints are re-ordered as follows: the purely unbounded + // constraints, the mixed unbounded + LCP constraints, and last the purely + // LCP constraints. this assists the LCP solver to put all unbounded + // variables at the start for a quick factorization. + // + // joints with m=0 are inactive and are removed from the joints array + // entirely, so that the code that follows does not consider them. + // also number all active joints in the joint list (set their tag values). + // inactive joints receive a tag value of -1. + + int m = 0; + dxJoint::Info1 * info = (dxJoint::Info1 *) ALLOCA (nj * sizeof (dxJoint::Info1)); + int *ofs = (int *) ALLOCA (nj * sizeof (int)); + for (i = 0, j = 0; j < nj; j++) + { // i=dest, j=src + joints[j]->vtable->getInfo1 (joints[j], info + i); + dIASSERT (info[i].m >= 0 && info[i].m <= 6 && info[i].nub >= 0 && info[i].nub <= info[i].m); + if (info[i].m > 0) + { + joints[i] = joints[j]; + joints[i]->tag = i; + i++; + } + else + { + joints[j]->tag = -1; + } + } + nj = i; + + // the purely unbounded constraints + for (i = 0; i < nj; i++) + { + ofs[i] = m; + m += info[i].m; + } + dReal *c = NULL; + dReal *cfm = NULL; + dReal *lo = NULL; + dReal *hi = NULL; + int *findex = NULL; + + dReal *J = NULL; + dxJoint::Info2 * Jinfo = NULL; + + if (m) + { + // create a constraint equation right hand side vector `c', a constraint + // force mixing vector `cfm', and LCP low and high bound vectors, and an + // 'findex' vector. + c = (dReal *) ALLOCA (m * sizeof (dReal)); + cfm = (dReal *) ALLOCA (m * sizeof (dReal)); + lo = (dReal *) ALLOCA (m * sizeof (dReal)); + hi = (dReal *) ALLOCA (m * sizeof (dReal)); + findex = (int *) ALLOCA (m * sizeof (int)); + dSetZero (c, m); + dSetValue (cfm, m, world->global_cfm); + dSetValue (lo, m, -dInfinity); + dSetValue (hi, m, dInfinity); + for (i = 0; i < m; i++) + findex[i] = -1; + + // get jacobian data from constraints. a (2*m)x8 matrix will be created + // to store the two jacobian blocks from each constraint. it has this + // format: + // + // l l l 0 a a a 0 \ . + // l l l 0 a a a 0 }-- jacobian body 1 block for joint 0 (3 rows) + // l l l 0 a a a 0 / + // l l l 0 a a a 0 \ . + // l l l 0 a a a 0 }-- jacobian body 2 block for joint 0 (3 rows) + // l l l 0 a a a 0 / + // l l l 0 a a a 0 }--- jacobian body 1 block for joint 1 (1 row) + // l l l 0 a a a 0 }--- jacobian body 2 block for joint 1 (1 row) + // etc... + // + // (lll) = linear jacobian data + // (aaa) = angular jacobian data + // +# ifdef TIMING + dTimerNow ("create J"); +# endif + J = (dReal *) ALLOCA (2 * m * 8 * sizeof (dReal)); + dSetZero (J, 2 * m * 8); + Jinfo = (dxJoint::Info2 *) ALLOCA (nj * sizeof (dxJoint::Info2)); + for (i = 0; i < nj; i++) + { + Jinfo[i].rowskip = 8; + Jinfo[i].fps = dRecip (stepsize); + Jinfo[i].erp = world->global_erp; + Jinfo[i].J1l = J + 2 * 8 * ofs[i]; + Jinfo[i].J1a = Jinfo[i].J1l + 4; + Jinfo[i].J2l = Jinfo[i].J1l + 8 * info[i].m; + Jinfo[i].J2a = Jinfo[i].J2l + 4; + Jinfo[i].c = c + ofs[i]; + Jinfo[i].cfm = cfm + ofs[i]; + Jinfo[i].lo = lo + ofs[i]; + Jinfo[i].hi = hi + ofs[i]; + Jinfo[i].findex = findex + ofs[i]; + //joints[i]->vtable->getInfo2 (joints[i], Jinfo+i); + } + + } + + dReal *saveFacc = (dReal *) ALLOCA (nb * 4 * sizeof (dReal)); + dReal *saveTacc = (dReal *) ALLOCA (nb * 4 * sizeof (dReal)); + dReal *globalI = (dReal *) ALLOCA (nb * 12 * sizeof (dReal)); + dReal *globalInvI = (dReal *) ALLOCA (nb * 12 * sizeof (dReal)); + for (b = 0; b < nb; b++) + { + for (i = 0; i < 4; i++) + { + saveFacc[b * 4 + i] = bodies[b]->facc[i]; + saveTacc[b * 4 + i] = bodies[b]->tacc[i]; + } + bodies[b]->tag = b; + } + + for (iter = 0; iter < maxiterations; iter++) + { +# ifdef TIMING + dTimerNow ("applying inertia and gravity"); +# endif + dReal tmp[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + for (b = 0; b < nb; b++) + { + body = bodies[b]; + + // for all bodies, compute the inertia tensor and its inverse in the global + // frame, and compute the rotational force and add it to the torque + // accumulator. I and invI are vertically stacked 3x4 matrices, one per body. + // @@@ check computation of rotational force. + + // compute inertia tensor in global frame + dMULTIPLY2_333 (tmp, body->mass.I, body->R); + dMULTIPLY0_333 (globalI + b * 12, body->R, tmp); + // compute inverse inertia tensor in global frame + dMULTIPLY2_333 (tmp, body->invI, body->R); + dMULTIPLY0_333 (globalInvI + b * 12, body->R, tmp); + + for (i = 0; i < 4; i++) + body->tacc[i] = saveTacc[b * 4 + i]; + // compute rotational force + dMULTIPLY0_331 (tmp, globalI + b * 12, body->avel); + dCROSS (body->tacc, -=, body->avel, tmp); + + // add the gravity force to all bodies + if ((body->flags & dxBodyNoGravity) == 0) + { + body->facc[0] = saveFacc[b * 4 + 0] + body->mass.mass * world->gravity[0]; + body->facc[1] = saveFacc[b * 4 + 1] + body->mass.mass * world->gravity[1]; + body->facc[2] = saveFacc[b * 4 + 2] + body->mass.mass * world->gravity[2]; + body->facc[3] = 0; + } else { + body->facc[0] = saveFacc[b * 4 + 0]; + body->facc[1] = saveFacc[b * 4 + 1]; + body->facc[2] = saveFacc[b * 4 + 2]; + body->facc[3] = 0; + } + + } + +#ifdef RANDOM_JOINT_ORDER +#ifdef TIMING + dTimerNow ("randomizing joint order"); +#endif + //randomize the order of the joints by looping through the array + //and swapping the current joint pointer with a random one before it. + for (j = 0; j < nj; j++) + { + joint = joints[j]; + dxJoint::Info1 i1 = info[j]; + dxJoint::Info2 i2 = Jinfo[j]; + const int r = dRandInt(j+1); + joints[j] = joints[r]; + info[j] = info[r]; + Jinfo[j] = Jinfo[r]; + joints[r] = joint; + info[r] = i1; + Jinfo[r] = i2; + } +#endif + + //now iterate through the random ordered joint array we created. + for (j = 0; j < nj; j++) + { +#ifdef TIMING + dTimerNow ("setting up joint"); +#endif + joint = joints[j]; + bodyPair[0] = joint->node[0].body; + bodyPair[1] = joint->node[1].body; + + if (bodyPair[0] && (bodyPair[0]->flags & dxBodyDisabled)) + bodyPair[0] = 0; + if (bodyPair[1] && (bodyPair[1]->flags & dxBodyDisabled)) + bodyPair[1] = 0; + + //if this joint is not connected to any enabled bodies, skip it. + if (!bodyPair[0] && !bodyPair[1]) + continue; + + if (bodyPair[0]) + { + GIPair[0] = globalI + bodyPair[0]->tag * 12; + GinvIPair[0] = globalInvI + bodyPair[0]->tag * 12; + } + if (bodyPair[1]) + { + GIPair[1] = globalI + bodyPair[1]->tag * 12; + GinvIPair[1] = globalInvI + bodyPair[1]->tag * 12; + } + + joints[j]->vtable->getInfo2 (joints[j], Jinfo + j); + + //dInternalStepIslandFast is an exact copy of the old routine with one + //modification: the calculated forces are added back to the facc and tacc + //vectors instead of applying them to the bodies and moving them. + if (info[j].m > 0) + { + dInternalStepFast (world, bodyPair, GIPair, GinvIPair, joint, info[j], Jinfo[j], ministep); + } + } + // } +# ifdef TIMING + dTimerNow ("moving bodies"); +# endif + //Now we can simulate all the free floating bodies, and move them. + for (b = 0; b < nb; b++) + { + body = bodies[b]; + + for (i = 0; i < 4; i++) + { + body->facc[i] *= ministep; + body->tacc[i] *= ministep; + } + + //apply torque + dMULTIPLYADD0_331 (body->avel, globalInvI + b * 12, body->tacc); + + //apply force + for (i = 0; i < 3; i++) + body->lvel[i] += body->invMass * body->facc[i]; + + //move It! + moveAndRotateBody (body, ministep); + } + } + for (b = 0; b < nb; b++) + for (j = 0; j < 4; j++) + bodies[b]->facc[j] = bodies[b]->tacc[j] = 0; +} + + +#ifdef NO_ISLANDS + +// Since the iterative algorithm doesn't care about islands of bodies, this is a +// faster algorithm that just sends it all the joints and bodies in one array. +// It's downfall is it's inability to handle disabled bodies as well as the old one. +static void +processIslandsFast (dxWorld * world, dReal stepsize, int maxiterations) +{ + // nothing to do if no bodies + if (world->nb <= 0) + return; + + dInternalHandleAutoDisabling (world,stepsize); + +# ifdef TIMING + dTimerStart ("creating joint and body arrays"); +# endif + dxBody **bodies, *body; + dxJoint **joints, *joint; + joints = (dxJoint **) ALLOCA (world->nj * sizeof (dxJoint *)); + bodies = (dxBody **) ALLOCA (world->nb * sizeof (dxBody *)); + + int nj = 0; + for (joint = world->firstjoint; joint; joint = (dxJoint *) joint->next) + joints[nj++] = joint; + + int nb = 0; + for (body = world->firstbody; body; body = (dxBody *) body->next) + bodies[nb++] = body; + + dInternalStepIslandFast (world, bodies, nb, joints, nj, stepsize, maxiterations); +# ifdef TIMING + dTimerEnd (); + dTimerReport (stdout, 1); +# endif +} + +#else + +//**************************************************************************** +// island processing + +// this groups all joints and bodies in a world into islands. all objects +// in an island are reachable by going through connected bodies and joints. +// each island can be simulated separately. +// note that joints that are not attached to anything will not be included +// in any island, an so they do not affect the simulation. +// +// this function starts new island from unvisited bodies. however, it will +// never start a new islands from a disabled body. thus islands of disabled +// bodies will not be included in the simulation. disabled bodies are +// re-enabled if they are found to be part of an active island. + +static void +processIslandsFast (dxWorld * world, dReal stepsize, int maxiterations) +{ +#ifdef TIMING + dTimerStart ("Island Setup"); +#endif + dxBody *b, *bb, **body; + dxJoint *j, **joint; + + // nothing to do if no bodies + if (world->nb <= 0) + return; + + dInternalHandleAutoDisabling (world,stepsize); + + // make arrays for body and joint lists (for a single island) to go into + body = (dxBody **) ALLOCA (world->nb * sizeof (dxBody *)); + joint = (dxJoint **) ALLOCA (world->nj * sizeof (dxJoint *)); + int bcount = 0; // number of bodies in `body' + int jcount = 0; // number of joints in `joint' + int tbcount = 0; + int tjcount = 0; + + // set all body/joint tags to 0 + for (b = world->firstbody; b; b = (dxBody *) b->next) + b->tag = 0; + for (j = world->firstjoint; j; j = (dxJoint *) j->next) + j->tag = 0; + + // allocate a stack of unvisited bodies in the island. the maximum size of + // the stack can be the lesser of the number of bodies or joints, because + // new bodies are only ever added to the stack by going through untagged + // joints. all the bodies in the stack must be tagged! + int stackalloc = (world->nj < world->nb) ? world->nj : world->nb; + dxBody **stack = (dxBody **) ALLOCA (stackalloc * sizeof (dxBody *)); + int *autostack = (int *) ALLOCA (stackalloc * sizeof (int)); + + for (bb = world->firstbody; bb; bb = (dxBody *) bb->next) + { +#ifdef TIMING + dTimerNow ("Island Processing"); +#endif + // get bb = the next enabled, untagged body, and tag it + if (bb->tag || (bb->flags & dxBodyDisabled)) + continue; + bb->tag = 1; + + // tag all bodies and joints starting from bb. + int stacksize = 0; + int autoDepth = autoEnableDepth; + b = bb; + body[0] = bb; + bcount = 1; + jcount = 0; + goto quickstart; + while (stacksize > 0) + { + b = stack[--stacksize]; // pop body off stack + autoDepth = autostack[stacksize]; + body[bcount++] = b; // put body on body list + quickstart: + + // traverse and tag all body's joints, add untagged connected bodies + // to stack + for (dxJointNode * n = b->firstjoint; n; n = n->next) + { + if (!n->joint->tag) + { + int thisDepth = autoEnableDepth; + n->joint->tag = 1; + joint[jcount++] = n->joint; + if (n->body && !n->body->tag) + { + if (n->body->flags & dxBodyDisabled) + thisDepth = autoDepth - 1; + if (thisDepth < 0) + continue; + n->body->flags &= ~dxBodyDisabled; + n->body->tag = 1; + autostack[stacksize] = thisDepth; + stack[stacksize++] = n->body; + } + } + } + dIASSERT (stacksize <= world->nb); + dIASSERT (stacksize <= world->nj); + } + + // now do something with body and joint lists + dInternalStepIslandFast (world, body, bcount, joint, jcount, stepsize, maxiterations); + + // what we've just done may have altered the body/joint tag values. + // we must make sure that these tags are nonzero. + // also make sure all bodies are in the enabled state. + int i; + for (i = 0; i < bcount; i++) + { + body[i]->tag = 1; + body[i]->flags &= ~dxBodyDisabled; + } + for (i = 0; i < jcount; i++) + joint[i]->tag = 1; + + tbcount += bcount; + tjcount += jcount; + } + +#ifdef TIMING + dMessage(0, "Total joints processed: %i, bodies: %i", tjcount, tbcount); +#endif + + // if debugging, check that all objects (except for disabled bodies, + // unconnected joints, and joints that are connected to disabled bodies) + // were tagged. +# ifndef dNODEBUG + for (b = world->firstbody; b; b = (dxBody *) b->next) + { + if (b->flags & dxBodyDisabled) + { + if (b->tag) + dDebug (0, "disabled body tagged"); + } + else + { + if (!b->tag) + dDebug (0, "enabled body not tagged"); + } + } + for (j = world->firstjoint; j; j = (dxJoint *) j->next) + { + if ((j->node[0].body && (j->node[0].body->flags & dxBodyDisabled) == 0) || (j->node[1].body && (j->node[1].body->flags & dxBodyDisabled) == 0)) + { + if (!j->tag) + dDebug (0, "attached enabled joint not tagged"); + } + else + { + if (j->tag) + dDebug (0, "unattached or disabled joint tagged"); + } + } +# endif + +# ifdef TIMING + dTimerEnd (); + dTimerReport (stdout, 1); +# endif +} + +#endif + + +void dWorldStepFast1 (dWorldID w, dReal stepsize, int maxiterations) +{ + dUASSERT (w, "bad world argument"); + dUASSERT (stepsize > 0, "stepsize must be > 0"); + processIslandsFast (w, stepsize, maxiterations); +} diff --git a/HenocUniverse/ode/source/testing.cpp b/HenocUniverse/ode/source/testing.cpp new file mode 100755 index 0000000..d55afc2 --- /dev/null +++ b/HenocUniverse/ode/source/testing.cpp @@ -0,0 +1,243 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#include +#include +#include +#include "testing.h" + +#ifdef dDOUBLE +static const dReal tol = 1.0e-9; +#else +static const dReal tol = 1.0e-5f; +#endif + + +// matrix header on the stack + +struct dMatrixComparison::dMatInfo { + int n,m; // size of matrix + char name[128]; // name of the matrix + dReal *data; // matrix data + int size; // size of `data' +}; + + + +dMatrixComparison::dMatrixComparison() +{ + afterfirst = 0; + index = 0; +} + + +dMatrixComparison::~dMatrixComparison() +{ + reset(); +} + + +dReal dMatrixComparison::nextMatrix (dReal *A, int n, int m, int lower_tri, + char *name, ...) +{ + if (A==0 || n < 1 || m < 1 || name==0) dDebug (0,"bad args to nextMatrix"); + int num = n*dPAD(m); + + if (afterfirst==0) { + dMatInfo *mi = (dMatInfo*) dAlloc (sizeof(dMatInfo)); + mi->n = n; + mi->m = m; + mi->size = num * sizeof(dReal); + mi->data = (dReal*) dAlloc (mi->size); + memcpy (mi->data,A,mi->size); + + va_list ap; + va_start (ap,name); + vsprintf (mi->name,name,ap); + if (strlen(mi->name) >= sizeof (mi->name)) dDebug (0,"name too long"); + + mat.push (mi); + return 0; + } + else { + if (lower_tri && n != m) + dDebug (0,"dMatrixComparison, lower triangular matrix must be square"); + if (index >= mat.size()) dDebug (0,"dMatrixComparison, too many matrices"); + dMatInfo *mp = mat[index]; + index++; + + dMatInfo mi; + va_list ap; + va_start (ap,name); + vsprintf (mi.name,name,ap); + if (strlen(mi.name) >= sizeof (mi.name)) dDebug (0,"name too long"); + + if (strcmp(mp->name,mi.name) != 0) + dDebug (0,"dMatrixComparison, name mismatch (\"%s\" and \"%s\")", + mp->name,mi.name); + if (mp->n != n || mp->m != m) + dDebug (0,"dMatrixComparison, size mismatch (%dx%d and %dx%d)", + mp->n,mp->m,n,m); + + dReal maxdiff; + if (lower_tri) { + maxdiff = dMaxDifferenceLowerTriangle (A,mp->data,n); + } + else { + maxdiff = dMaxDifference (A,mp->data,n,m); + } + if (maxdiff > tol) + dDebug (0,"dMatrixComparison, matrix error (size=%dx%d, name=\"%s\", " + "error=%.4e)",n,m,mi.name,maxdiff); + return maxdiff; + } +} + + +void dMatrixComparison::end() +{ + if (mat.size() <= 0) dDebug (0,"no matrices in sequence"); + afterfirst = 1; + index = 0; +} + + +void dMatrixComparison::reset() +{ + for (int i=0; idata,mat[i]->size); + dFree (mat[i],sizeof(dMatInfo)); + } + mat.setSize (0); + afterfirst = 0; + index = 0; +} + + +void dMatrixComparison::dump() +{ + for (int i=0; iname,mat[i]->n,mat[i]->m); +} + +//**************************************************************************** +// unit test + +#include + +static jmp_buf jump_buffer; + +static void myDebug (int num, const char *msg, va_list ap) +{ + // printf ("(Error %d: ",num); + // vprintf (msg,ap); + // printf (")\n"); + longjmp (jump_buffer,1); +} + + +extern "C" void dTestMatrixComparison() +{ + volatile int i; + printf ("dTestMatrixComparison()\n"); + dMessageFunction *orig_debug = dGetDebugHandler(); + + dMatrixComparison mc; + dReal A[50*50]; + + // make first sequence + unsigned long seed = dRandGetSeed(); + for (i=1; i<49; i++) { + dMakeRandomMatrix (A,i,i+1,1.0); + mc.nextMatrix (A,i,i+1,0,"A%d",i); + } + mc.end(); + + //mc.dump(); + + // test identical sequence + dSetDebugHandler (&myDebug); + dRandSetSeed (seed); + if (setjmp (jump_buffer)) { + printf ("\tFAILED (1)\n"); + } + else { + for (i=1; i<49; i++) { + dMakeRandomMatrix (A,i,i+1,1.0); + mc.nextMatrix (A,i,i+1,0,"A%d",i); + } + mc.end(); + printf ("\tpassed (1)\n"); + } + dSetDebugHandler (orig_debug); + + // test broken sequences (with matrix error) + dRandSetSeed (seed); + volatile int passcount = 0; + for (i=1; i<49; i++) { + if (setjmp (jump_buffer)) { + passcount++; + } + else { + dSetDebugHandler (&myDebug); + dMakeRandomMatrix (A,i,i+1,1.0); + A[(i-1)*dPAD(i+1)+i] += REAL(0.01); + mc.nextMatrix (A,i,i+1,0,"A%d",i); + dSetDebugHandler (orig_debug); + } + } + mc.end(); + printf ("\t%s (2)\n",(passcount == 48) ? "passed" : "FAILED"); + + // test broken sequences (with name error) + dRandSetSeed (seed); + passcount = 0; + for (i=1; i<49; i++) { + if (setjmp (jump_buffer)) { + passcount++; + } + else { + dSetDebugHandler (&myDebug); + dMakeRandomMatrix (A,i,i+1,1.0); + mc.nextMatrix (A,i,i+1,0,"B%d",i); + dSetDebugHandler (orig_debug); + } + } + mc.end(); + printf ("\t%s (3)\n",(passcount == 48) ? "passed" : "FAILED"); + + // test identical sequence again + dSetDebugHandler (&myDebug); + dRandSetSeed (seed); + if (setjmp (jump_buffer)) { + printf ("\tFAILED (4)\n"); + } + else { + for (i=1; i<49; i++) { + dMakeRandomMatrix (A,i,i+1,1.0); + mc.nextMatrix (A,i,i+1,0,"A%d",i); + } + mc.end(); + printf ("\tpassed (4)\n"); + } + dSetDebugHandler (orig_debug); +} diff --git a/HenocUniverse/ode/source/testing.h b/HenocUniverse/ode/source/testing.h new file mode 100755 index 0000000..4d19ff3 --- /dev/null +++ b/HenocUniverse/ode/source/testing.h @@ -0,0 +1,65 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* stuff used for testing */ + +#ifndef _ODE_TESTING_H_ +#define _ODE_TESTING_H_ + +#include +#include "array.h" + + +// compare a sequence of named matrices/vectors, i.e. to make sure that two +// different pieces of code are giving the same results. + +class dMatrixComparison { + struct dMatInfo; + dArray mat; + int afterfirst,index; + +public: + dMatrixComparison(); + ~dMatrixComparison(); + + dReal nextMatrix (dReal *A, int n, int m, int lower_tri, char *name, ...); + // add a new n*m matrix A to the sequence. the name of the matrix is given + // by the printf-style arguments (name,...). if this is the first sequence + // then this object will simply record the matrices and return 0. + // if this the second or subsequent sequence then this object will compare + // the matrices with the first sequence, and report any differences. + // the matrix error will be returned. if `lower_tri' is 1 then only the + // lower triangle of the matrix (including the diagonal) will be compared + // (the matrix must be square). + + void end(); + // end a sequence. + + void reset(); + // restarts the object, so the next sequence will be the first sequence. + + void dump(); + // print out info about all the matrices in the sequence +}; + + +#endif diff --git a/HenocUniverse/ode/source/timer.cpp b/HenocUniverse/ode/source/timer.cpp new file mode 100755 index 0000000..857c65c --- /dev/null +++ b/HenocUniverse/ode/source/timer.cpp @@ -0,0 +1,399 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* + +---- + +* gettimeofday() and the pentium time stamp counter return the real time, + not the process time. fix this somehow! + +*/ + +#include +#include +#include +#include + +// misc defines +#define ALLOCA dALLOCA16 + +//**************************************************************************** +// implementation for windows based on the multimedia performance counter. + +#ifdef WIN32 + +#include "windows.h" + +static inline void getClockCount (unsigned long cc[2]) +{ + LARGE_INTEGER a; + QueryPerformanceCounter (&a); + cc[0] = a.LowPart; + cc[1] = a.HighPart; +} + + +static inline void serialize() +{ +} + + +static inline double loadClockCount (unsigned long cc[2]) +{ + LARGE_INTEGER a; + a.LowPart = cc[0]; + a.HighPart = cc[1]; + return double(a.QuadPart); +} + + +double dTimerResolution() +{ + return 1.0/dTimerTicksPerSecond(); +} + + +double dTimerTicksPerSecond() +{ + static int query=0; + static double hz=0.0; + if (!query) { + LARGE_INTEGER a; + QueryPerformanceFrequency (&a); + hz = double(a.QuadPart); + query = 1; + } + return hz; +} + +#endif + +//**************************************************************************** +// implementation based on the pentium time stamp counter. the timer functions +// can be serializing or non-serializing. serializing will ensure that all +// instructions have executed and data has been written back before the cpu +// time stamp counter is read. the CPUID instruction is used to serialize. + +#if defined(PENTIUM) && !defined(WIN32) + +// we need to know the clock rate so that the timing function can report +// accurate times. this number only needs to be set accurately if we're +// doing performance tests and care about real-world time numbers - otherwise, +// just ignore this. i have not worked out how to determine this number +// automatically yet. + +#define PENTIUM_HZ (500e6) + + +static inline void getClockCount (unsigned long cc[2]) +{ + asm volatile ( + "rdtsc\n" + "movl %%eax,(%%esi)\n" + "movl %%edx,4(%%esi)\n" + : : "S" (cc) : "%eax","%edx","cc","memory"); +} + + +static inline void serialize() +{ + asm volatile ( + "mov $0,%%eax\n" + "cpuid\n" + : : : "%eax","%ebx","%ecx","%edx","cc","memory"); +} + + +static inline double loadClockCount (unsigned long a[2]) +{ + double ret; + asm volatile ("fildll %1; fstpl %0" : "=m" (ret) : "m" (a[0]) : + "cc","memory"); + return ret; +} + + +double dTimerResolution() +{ + return 1.0/PENTIUM_HZ; +} + + +double dTimerTicksPerSecond() +{ + return PENTIUM_HZ; +} + +#endif + +//**************************************************************************** +// otherwise, do the implementation based on gettimeofday(). + +#if !defined(PENTIUM) && !defined(WIN32) + +#ifndef macintosh + +#include +#include + + +static inline void getClockCount (unsigned long cc[2]) +{ + struct timeval tv; + gettimeofday (&tv,0); + cc[0] = tv.tv_usec; + cc[1] = tv.tv_sec; +} + +#else // macintosh + +#include +#include + +static inline void getClockCount (unsigned long cc[2]) +{ + UnsignedWide ms; + Microseconds (&ms); + cc[1] = ms.lo / 1000000; + cc[0] = ms.lo - ( cc[1] * 1000000 ); +} + +#endif + + +static inline void serialize() +{ +} + + +static inline double loadClockCount (unsigned long a[2]) +{ + return a[1]*1.0e6 + a[0]; +} + + +double dTimerResolution() +{ + unsigned long cc1[2],cc2[2]; + getClockCount (cc1); + do { + getClockCount (cc2); + } + while (cc1[0]==cc2[0] && cc1[1]==cc2[1]); + do { + getClockCount (cc1); + } + while (cc1[0]==cc2[0] && cc1[1]==cc2[1]); + double t1 = loadClockCount (cc1); + double t2 = loadClockCount (cc2); + return (t1-t2) / dTimerTicksPerSecond(); +} + + +double dTimerTicksPerSecond() +{ + return 1000000; +} + +#endif + +//**************************************************************************** +// stop watches + +void dStopwatchReset (dStopwatch *s) +{ + s->time = 0; + s->cc[0] = 0; + s->cc[1] = 0; +} + + +void dStopwatchStart (dStopwatch *s) +{ + serialize(); + getClockCount (s->cc); +} + + +void dStopwatchStop (dStopwatch *s) +{ + unsigned long cc[2]; + serialize(); + getClockCount (cc); + double t1 = loadClockCount (s->cc); + double t2 = loadClockCount (cc); + s->time += t2-t1; +} + + +double dStopwatchTime (dStopwatch *s) +{ + return s->time / dTimerTicksPerSecond(); +} + +//**************************************************************************** +// code timers + +// maximum number of events to record +#define MAXNUM 100 + +static int num = 0; // number of entries used in event array +static struct { + unsigned long cc[2]; // clock counts + double total_t; // total clocks used in this slot. + double total_p; // total percentage points used in this slot. + int count; // number of times this slot has been updated. + char *description; // pointer to static string +} event[MAXNUM]; + + +// make sure all slot totals and counts reset to 0 at start + +static void initSlots() +{ + static int initialized=0; + if (!initialized) { + for (int i=0; i (description); + num = 1; + serialize(); + getClockCount (event[0].cc); +} + + +void dTimerNow (const char *description) +{ + if (num < MAXNUM) { + // do not serialize + getClockCount (event[num].cc); + event[num].description = const_cast (description); + num++; + } +} + + +void dTimerEnd() +{ + if (num < MAXNUM) { + serialize(); + getClockCount (event[num].cc); + event[num].description = "TOTAL"; + num++; + } +} + +//**************************************************************************** +// print report + +static void fprintDoubleWithPrefix (FILE *f, double a, char *fmt) +{ + if (a >= 0.999999) { + fprintf (f,fmt,a); + return; + } + a *= 1000.0; + if (a >= 0.999999) { + fprintf (f,fmt,a); + fprintf (f,"m"); + return; + } + a *= 1000.0; + if (a >= 0.999999) { + fprintf (f,fmt,a); + fprintf (f,"u"); + return; + } + a *= 1000.0; + fprintf (f,fmt,a); + fprintf (f,"n"); +} + + +void dTimerReport (FILE *fout, int average) +{ + int i; + size_t maxl; + double ccunit = 1.0/dTimerTicksPerSecond(); + fprintf (fout,"\nTimer Report ("); + fprintDoubleWithPrefix (fout,ccunit,"%.2f "); + fprintf (fout,"s resolution)\n------------\n"); + if (num < 1) return; + + // get maximum description length + maxl = 0; + for (i=0; i maxl) maxl = l; + } + + // calculate total time + double t1 = loadClockCount (event[0].cc); + double t2 = loadClockCount (event[num-1].cc); + double total = t2 - t1; + if (total <= 0) total = 1; + + // compute time difference for all slots except the last one. update totals + double *times = (double*) ALLOCA (num * sizeof(double)); + for (i=0; i < (num-1); i++) { + double t1 = loadClockCount (event[i].cc); + double t2 = loadClockCount (event[i+1].cc); + times[i] = t2 - t1; + event[i].count++; + event[i].total_t += times[i]; + event[i].total_p += times[i]/total * 100.0; + } + + // print report (with optional averages) + for (i=0; i +#include + +#define ALLOCA dALLOCA16 + +//**************************************************************************** +// Auto disabling + +void dInternalHandleAutoDisabling (dxWorld *world, dReal stepsize) +{ + dxBody *bb; + for (bb=world->firstbody; bb; bb=(dxBody*)bb->next) { + // nothing to do unless this body is currently enabled and has + // the auto-disable flag set + if ((bb->flags & (dxBodyAutoDisable|dxBodyDisabled)) != dxBodyAutoDisable) continue; + + // see if the body is idle + int idle = 1; // initial assumption + dReal lspeed2 = dDOT(bb->lvel,bb->lvel); + if (lspeed2 > bb->adis.linear_threshold) { + idle = 0; // moving fast - not idle + } + else { + dReal aspeed = dDOT(bb->avel,bb->avel); + if (aspeed > bb->adis.angular_threshold) { + idle = 0; // turning fast - not idle + } + } + + // if it's idle, accumulate steps and time. + // these counters won't overflow because this code doesn't run for disabled bodies. + if (idle) { + bb->adis_stepsleft--; + bb->adis_timeleft -= stepsize; + } + else { + bb->adis_stepsleft = bb->adis.idle_steps; + bb->adis_timeleft = bb->adis.idle_time; + } + + // disable the body if it's idle for a long enough time + if (bb->adis_stepsleft < 0 && bb->adis_timeleft < 0) { + bb->flags |= dxBodyDisabled; + } + } +} + + +//**************************************************************************** +// body rotation + +// return sin(x)/x. this has a singularity at 0 so special handling is needed +// for small arguments. + +static inline dReal sinc (dReal x) +{ + // if |x| < 1e-4 then use a taylor series expansion. this two term expansion + // is actually accurate to one LS bit within this range if double precision + // is being used - so don't worry! + if (dFabs(x) < 1.0e-4) return REAL(1.0) - x*x*REAL(0.166666666666666666667); + else return dSin(x)/x; +} + + +// given a body b, apply its linear and angular rotation over the time +// interval h, thereby adjusting its position and orientation. + +void dxStepBody (dxBody *b, dReal h) +{ + int j; + + // handle linear velocity + for (j=0; j<3; j++) b->pos[j] += h * b->lvel[j]; + + if (b->flags & dxBodyFlagFiniteRotation) { + dVector3 irv; // infitesimal rotation vector + dQuaternion q; // quaternion for finite rotation + + if (b->flags & dxBodyFlagFiniteRotationAxis) { + // split the angular velocity vector into a component along the finite + // rotation axis, and a component orthogonal to it. + dVector3 frv; // finite rotation vector + dReal k = dDOT (b->finite_rot_axis,b->avel); + frv[0] = b->finite_rot_axis[0] * k; + frv[1] = b->finite_rot_axis[1] * k; + frv[2] = b->finite_rot_axis[2] * k; + irv[0] = b->avel[0] - frv[0]; + irv[1] = b->avel[1] - frv[1]; + irv[2] = b->avel[2] - frv[2]; + + // make a rotation quaternion q that corresponds to frv * h. + // compare this with the full-finite-rotation case below. + h *= REAL(0.5); + dReal theta = k * h; + q[0] = dCos(theta); + dReal s = sinc(theta) * h; + q[1] = frv[0] * s; + q[2] = frv[1] * s; + q[3] = frv[2] * s; + } + else { + // make a rotation quaternion q that corresponds to w * h + dReal wlen = dSqrt (b->avel[0]*b->avel[0] + b->avel[1]*b->avel[1] + + b->avel[2]*b->avel[2]); + h *= REAL(0.5); + dReal theta = wlen * h; + q[0] = dCos(theta); + dReal s = sinc(theta) * h; + q[1] = b->avel[0] * s; + q[2] = b->avel[1] * s; + q[3] = b->avel[2] * s; + } + + // do the finite rotation + dQuaternion q2; + dQMultiply0 (q2,q,b->q); + for (j=0; j<4; j++) b->q[j] = q2[j]; + + // do the infitesimal rotation if required + if (b->flags & dxBodyFlagFiniteRotationAxis) { + dReal dq[4]; + dWtoDQ (irv,b->q,dq); + for (j=0; j<4; j++) b->q[j] += h * dq[j]; + } + } + else { + // the normal way - do an infitesimal rotation + dReal dq[4]; + dWtoDQ (b->avel,b->q,dq); + for (j=0; j<4; j++) b->q[j] += h * dq[j]; + } + + // normalize the quaternion and convert it to a rotation matrix + dNormalize4 (b->q); + dQtoR (b->q,b->R); + + // notify all attached geoms that this body has moved + for (dxGeom *geom = b->geom; geom; geom = dGeomGetBodyNext (geom)) + dGeomMoved (geom); +} + +//**************************************************************************** +// island processing + +// this groups all joints and bodies in a world into islands. all objects +// in an island are reachable by going through connected bodies and joints. +// each island can be simulated separately. +// note that joints that are not attached to anything will not be included +// in any island, an so they do not affect the simulation. +// +// this function starts new island from unvisited bodies. however, it will +// never start a new islands from a disabled body. thus islands of disabled +// bodies will not be included in the simulation. disabled bodies are +// re-enabled if they are found to be part of an active island. + +void dxProcessIslands (dxWorld *world, dReal stepsize, dstepper_fn_t stepper) +{ + dxBody *b,*bb,**body; + dxJoint *j,**joint; + + // nothing to do if no bodies + if (world->nb <= 0) return; + + // handle auto-disabling of bodies + dInternalHandleAutoDisabling (world,stepsize); + + // make arrays for body and joint lists (for a single island) to go into + body = (dxBody**) ALLOCA (world->nb * sizeof(dxBody*)); + joint = (dxJoint**) ALLOCA (world->nj * sizeof(dxJoint*)); + int bcount = 0; // number of bodies in `body' + int jcount = 0; // number of joints in `joint' + + // set all body/joint tags to 0 + for (b=world->firstbody; b; b=(dxBody*)b->next) b->tag = 0; + for (j=world->firstjoint; j; j=(dxJoint*)j->next) j->tag = 0; + + // allocate a stack of unvisited bodies in the island. the maximum size of + // the stack can be the lesser of the number of bodies or joints, because + // new bodies are only ever added to the stack by going through untagged + // joints. all the bodies in the stack must be tagged! + int stackalloc = (world->nj < world->nb) ? world->nj : world->nb; + dxBody **stack = (dxBody**) ALLOCA (stackalloc * sizeof(dxBody*)); + + for (bb=world->firstbody; bb; bb=(dxBody*)bb->next) { + // get bb = the next enabled, untagged body, and tag it + if (bb->tag || (bb->flags & dxBodyDisabled)) continue; + bb->tag = 1; + + // tag all bodies and joints starting from bb. + int stacksize = 0; + b = bb; + body[0] = bb; + bcount = 1; + jcount = 0; + goto quickstart; + while (stacksize > 0) { + b = stack[--stacksize]; // pop body off stack + body[bcount++] = b; // put body on body list + quickstart: + + // traverse and tag all body's joints, add untagged connected bodies + // to stack + for (dxJointNode *n=b->firstjoint; n; n=n->next) { + if (!n->joint->tag) { + n->joint->tag = 1; + joint[jcount++] = n->joint; + if (n->body && !n->body->tag) { + n->body->tag = 1; + stack[stacksize++] = n->body; + } + } + } + dIASSERT(stacksize <= world->nb); + dIASSERT(stacksize <= world->nj); + } + + // now do something with body and joint lists + stepper (world,body,bcount,joint,jcount,stepsize); + + // what we've just done may have altered the body/joint tag values. + // we must make sure that these tags are nonzero. + // also make sure all bodies are in the enabled state. + int i; + for (i=0; itag = 1; + body[i]->flags &= ~dxBodyDisabled; + } + for (i=0; itag = 1; + } + + // if debugging, check that all objects (except for disabled bodies, + // unconnected joints, and joints that are connected to disabled bodies) + // were tagged. +# ifndef dNODEBUG + for (b=world->firstbody; b; b=(dxBody*)b->next) { + if (b->flags & dxBodyDisabled) { + if (b->tag) dDebug (0,"disabled body tagged"); + } + else { + if (!b->tag) dDebug (0,"enabled body not tagged"); + } + } + for (j=world->firstjoint; j; j=(dxJoint*)j->next) { + if ((j->node[0].body && (j->node[0].body->flags & dxBodyDisabled)==0) || + (j->node[1].body && (j->node[1].body->flags & dxBodyDisabled)==0)) { + if (!j->tag) dDebug (0,"attached enabled joint not tagged"); + } + else { + if (j->tag) dDebug (0,"unattached or disabled joint tagged"); + } + } +# endif +} diff --git a/HenocUniverse/ode/source/util.h b/HenocUniverse/ode/source/util.h new file mode 100755 index 0000000..a8e6390 --- /dev/null +++ b/HenocUniverse/ode/source/util.h @@ -0,0 +1,38 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef _ODE_UTIL_H_ +#define _ODE_UTIL_H_ + +#include "objects.h" + + +void dInternalHandleAutoDisabling (dxWorld *world, dReal stepsize); +void dxStepBody (dxBody *b, dReal h); + +typedef void (*dstepper_fn_t) (dxWorld *world, dxBody * const *body, int nb, + dxJoint * const *_joint, int nj, dReal stepsize); + +void dxProcessIslands (dxWorld *world, dReal stepsize, dstepper_fn_t stepper); + + +#endif diff --git a/HenocUniverse/ode/timer.h b/HenocUniverse/ode/timer.h new file mode 100755 index 0000000..dcae5b5 --- /dev/null +++ b/HenocUniverse/ode/timer.h @@ -0,0 +1,76 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * This library 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef _ODE_TIMER_H_ +#define _ODE_TIMER_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* stop watch objects */ + +typedef struct dStopwatch { + double time; /* total clock count */ + unsigned long cc[2]; /* clock count since last `start' */ +} dStopwatch; + +void dStopwatchReset (dStopwatch *); +void dStopwatchStart (dStopwatch *); +void dStopwatchStop (dStopwatch *); +double dStopwatchTime (dStopwatch *); /* returns total time in secs */ + + +/* code timers */ + +void dTimerStart (const char *description); /* pass a static string here */ +void dTimerNow (const char *description); /* pass a static string here */ +void dTimerEnd(); + +/* print out a timer report. if `average' is nonzero, print out the average + * time for each slot (this is only meaningful if the same start-now-end + * calls are being made repeatedly. + */ +void dTimerReport (FILE *fout, int average); + + +/* resolution */ + +/* returns the timer ticks per second implied by the timing hardware or API. + * the actual timer resolution may not be this great. + */ +double dTimerTicksPerSecond(); + +/* returns an estimate of the actual timer resolution, in seconds. this may + * be greater than 1/ticks_per_second. + */ +double dTimerResolution(); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/HenocUniverse/shapes.h b/HenocUniverse/shapes.h new file mode 100755 index 0000000..b450aa5 --- /dev/null +++ b/HenocUniverse/shapes.h @@ -0,0 +1,110 @@ +/* + * TT102 HENOC + * 2008 @ ESCOM-IPN + * + */ +#ifndef _HENOC_SHAPES_H +#define _HENOC_SHAPES_H + +#include +#include +#include +#include +#include + +namespace HenocUniverse{ + class Quad : public Geometry{ + public: + Quad(float width, float height); + void UpdateBounds(); + void SetMass(Body body, float density) const; + Shape GetShape() const { return Shape::Quad; } + virtual float Extent(int i) const { return extent[i]; } + vec2 EAxis(int i) const { return Axis(i) * extent[i]; } + const vec2* GetCorners() const { return corners; } + bool Contains(const vec2 &v) const; + protected: + float extent[2]; + vec2 corners[4]; + }; + + class Block : public Quad{ + public: + Block(vec2 topleft, vec2 bottomright); + Block(vec2 center, float width, float height); + Block(float left, float top, float right, float bottom); + }; + + class Line : public Quad{ + public: + Line(vec2 origin, vec2 end); + vec2 Origin() const { return center - axis * extent[0]; } + vec2 End() const { return center + axis * extent[0]; } + }; + + typedef std::vector VList; + typedef std::vector LineStrip; + typedef std::map SpanMap; + + class Terrain : public Geometry{ + public: + Terrain(const vec2 &start); + void UpdateBounds(); + void Finalize() { UpdateBounds(); } + Shape GetShape() const { return Shape::Terrain; } + bool GetIndexRange(float left, float right, int &lower, int &upper) const; + + typedef VList::const_iterator const_iterator; + const_iterator begin() const { return vertices.begin(); } + const_iterator end() const { return vertices.end(); } + const vec2 &front() const { return vertices.front(); } + const vec2 &back() const { return vertices.back(); } + void push_back(const vec2 &v); + size_t size() const { return lines.size(); } + bool empty() const { return lines.empty(); } + Line &operator[](int i) { return lines[i]; } + const Line &operator[](int i) const { return lines[i]; } + private: + vec2 previous; + LineStrip lines; + SpanMap spans; + VList vertices; + }; + + class Circle : public Geometry{ + public: + Circle(const vec2 ¢er, float radius); + void UpdateBounds(); + void SetMass(Body body, float density) const; + Shape GetShape() const { return Shape::Circle; } + float Radius() const { return radius; } + private: + float radius; + }; + + typedef std::list GList; + + class Composite : public Geometry{ + public: + Composite(vec2 centroid); + ~Composite(); + void UpdateBounds(); + void Finalize() { UpdateBounds(); } + Shape GetShape() const { return Shape::Composite; } + void SetCenter(const vec2 ¢er); + void SetAxis(const vec2 &axis); + void SetMass(Body body, float density) const; + typedef GList::const_iterator const_iterator; + typedef GList::iterator iterator; + const_iterator begin() const { return geometries.begin(); } + const_iterator end() const { return geometries.end(); } + const Geometry &front() const { return *geometries.front(); } + const Geometry &back() const { return *geometries.back(); } + void push_back(Geometry *geometry); + size_t size() const { return geometries.size(); } + private: + GList geometries; + }; +} + +#endif diff --git a/HenocUniverse/source/circle-circle.cpp b/HenocUniverse/source/circle-circle.cpp new file mode 100755 index 0000000..c81fbb4 --- /dev/null +++ b/HenocUniverse/source/circle-circle.cpp @@ -0,0 +1,47 @@ +/* + * TT102 HENOC + * 2008 @ ESCOM-IPN + * Intersecciones circulo-circulo y generador de contactos + */ + +#include +#include + +bool HenocUniverse::Intersection::TestCircleCircle(const Geometry &g1, const Geometry &g2){ + assert(g1.GetShape() == Shape::Circle); + assert(g2.GetShape() == Shape::Circle); + const Circle &c1 = static_cast(g1); + const Circle &c2 = static_cast(g2); + + float distance = (c2.Center() - c1.Center()).length(); + return distance <= c1.Radius() + c2.Radius(); +} + +void HenocUniverse::Intersection::FindCircleCircle(const Geometry &g1, const Geometry &g2, ContactList &contacts){ + assert(g1.GetShape() == Shape::Circle); + assert(g2.GetShape() == Shape::Circle); + const Circle &c1 = static_cast(g1); + const Circle &c2 = static_cast(g2); + + vec2 delta = c2.Center() - c1.Center(); + float radiusSum = c1.Radius() + c2.Radius(); + float distance = delta.length(); + if (distance > radiusSum) return; + + vec2 pos; + vec2 normal; + float depth; + + if (distance <= 0){ + pos = c1.Center(); + normal = vec2(1, 0); + depth = radiusSum; + } + else{ + normal = delta / distance; + float k = 0.5 * (c2.Radius() - c1.Radius() - distance); + pos = c1.Center() + normal * k; + depth = radiusSum - distance; + } + contacts.AddContact(pos, -normal, depth); +} diff --git a/HenocUniverse/source/composite-all.cpp b/HenocUniverse/source/composite-all.cpp new file mode 100755 index 0000000..cfa6c88 --- /dev/null +++ b/HenocUniverse/source/composite-all.cpp @@ -0,0 +1,94 @@ +// Summary: Test for composite-all intersection and generate contacts. +/* + * TT102 HENOC + * 2008 @ ESCOM-IPN + * Intersecciones compuestas y generador de contactos + */ + + +#include +#include + +using namespace HenocUniverse; + +bool TestCompositeGeometry(const Geometry &g1, const Geometry &g2){ + assert(g1.GetShape() == Shape::Composite); + const Composite &c = static_cast(g1); + + for (Composite::const_iterator g = c.begin(); g != c.end(); ++g) + if (Intersection::Test(**g, g2)) return true; + return false; +} + +void FindCompositeGeometry(const Geometry &g1, const Geometry &g2, ContactList &contacts){ + assert(g1.GetShape() == Shape::Composite); + const Composite &c = static_cast(g1); + + for (Composite::const_iterator g = c.begin(); g != c.end(); ++g){ + if (Intersection::Test(**g, g2)) + Intersection::Find(**g, g2, contacts); + } +} + +void Intersection::FindCompositeQuad(const Geometry &g1, const Geometry &g2, ContactList &contacts){ + FindCompositeGeometry(g1, g2, contacts); +} + +void Intersection::FindCompositeCircle(const Geometry &g1, const Geometry &g2, ContactList &contacts){ + FindCompositeGeometry(g1, g2, contacts); +} + +void Intersection::FindCompositeTerrain(const Geometry &g1, const Geometry &g2, ContactList &contacts){ + FindCompositeGeometry(g1, g2, contacts); +} + +void Intersection::FindCompositeComposite(const Geometry &g1, const Geometry &g2, ContactList &contacts){ + FindCompositeGeometry(g1, g2, contacts); +} + +void Intersection::FindQuadComposite(const Geometry &g1, const Geometry &g2, ContactList &contacts){ + contacts.ToggleNormalInversion(); + FindCompositeGeometry(g2, g1, contacts); + contacts.ToggleNormalInversion(); +} + +void Intersection::FindCircleComposite(const Geometry &g1, const Geometry &g2, ContactList &contacts){ + contacts.ToggleNormalInversion(); + FindCompositeGeometry(g2, g1, contacts); + contacts.ToggleNormalInversion(); +} + +void Intersection::FindTerrainComposite(const Geometry &g1, const Geometry &g2, ContactList &contacts){ + contacts.ToggleNormalInversion(); + FindCompositeGeometry(g2, g1, contacts); + contacts.ToggleNormalInversion(); +} + +bool Intersection::TestCompositeQuad(const Geometry &g1, const Geometry &g2){ + return TestCompositeGeometry(g1, g2); +} + +bool Intersection::TestCompositeCircle(const Geometry &g1, const Geometry &g2){ + return TestCompositeGeometry(g1, g2); +} + +bool Intersection::TestCompositeTerrain(const Geometry &g1, const Geometry &g2){ + return TestCompositeGeometry(g1, g2); +} + +bool Intersection::TestCompositeComposite(const Geometry &g1, const Geometry &g2){ + return TestCompositeGeometry(g1, g2); +} + +bool Intersection::TestQuadComposite(const Geometry &g1, const Geometry &g2){ + return TestCompositeGeometry(g2, g1); +} + +bool Intersection::TestCircleComposite(const Geometry &g1, const Geometry &g2){ + return TestCompositeGeometry(g2, g1); +} + +bool Intersection::TestTerrainComposite(const Geometry &g1, const Geometry &g2){ + return TestCompositeGeometry(g2, g1); +} + diff --git a/HenocUniverse/source/henocUniverse.cpp b/HenocUniverse/source/henocUniverse.cpp new file mode 100755 index 0000000..ea94a11 --- /dev/null +++ b/HenocUniverse/source/henocUniverse.cpp @@ -0,0 +1,275 @@ +/* + * TT102 HENOC + * 2008 @ ESCOM-IPN + * Define los metodos para las clases geometricas y la clase Wordl + */ + + +#include +#include +#include + +using namespace HenocUniverse; + +void dGeomMoved(dGeomID g) { g->Move(); } +dGeomID dGeomGetBodyNext(dGeomID) { return 0; } +void dGeomSetBody(dGeomID g, dBodyID b) {} + +ObjectProperties Object::defaults = { 5, dInfinity, 0.35, 0, ~0, ~0, 0 }; +std::stack Object::defaultStack; + +Object::Object() : properties(defaults) {} + +Geometry::Geometry(){ + axis = vec2(1, 0); + center = vec2(0, 0); + bounds = aabb(0,0,0,0); +} + +void Object::Move(){ + assert(IsDynamic() && GetBody()); + const float *R = dBodyGetRotation(GetBody()); + { + float c[3] = {1, 0, 0}; + float a[4]; + dMultiply0(a, R, c, 4, 3, 1); + GetGeometry().SetAxis(vec2(a)); + } + + const float *position = dBodyGetPosition(GetBody()); + GetGeometry().SetCenter(vec2(position)); + GetGeometry().UpdateBounds(); +} + +void Object::Rotate(float theta){ + GetGeometry().SetAxis(vec2(0, 1)); + GetGeometry().Rotate(theta); + if (IsDynamic() && GetBody()){ + const float degreesToRadians = atan(1.0f) / 45; + dMatrix3 R; + dRFromAxisAndAngle(R, 0, 0, 1, theta * degreesToRadians); + dBodySetRotation(GetBody(), R); + } +} + +void Object::SetCenter(const vec2 ¢er){ + GetGeometry().SetCenter(center); + if (IsDynamic() && GetBody()) + dBodySetPosition(GetBody(), center.x, center.y, 0); +} + +void Geometry::Rotate(float theta){ + SetAxis(this->axis.rotate(theta)); +} + +void Geometry::Rotate(const vec2 &xform){ + SetAxis(this->axis.rotate(xform)); +} + +void Geometry::SetCenter(const vec2 ¢er){ + this->center = center; +} + +void Geometry::SetAxis(const vec2 &axis){ + this->axis = axis; +} + +World::World(){ + world = dWorldCreate(); + contactGroup = dJointGroupCreate(0); + contactCount = 0; +} + +World::~World(){ + dJointGroupDestroy(contactGroup); + dWorldDestroy(world); +} + +void World::QuickStep(float f){ + dWorldQuickStep(world, f); +} + +Body World::BodyCreate(){ + return dBodyCreate(world); +} + +void World::SetAutoDisableLinearThreshold(float f){ + dWorldSetAutoDisableLinearThreshold(world, f); +} + +void World::SetAutoDisableAngularThreshold(float f){ + dWorldSetAutoDisableAngularThreshold(world, f); +} + +void World::SetCFM(float f){ + dWorldSetCFM(world, f); +} + +void World::SetAutoDisableFlag(bool b){ + dWorldSetAutoDisableFlag(world, b ? 1 : 0); +} + +void World::SetERP(float f){ + dWorldSetERP(world, f); +} + +void World::SetContactMaxCorrectingVel(float f){ + dWorldSetContactMaxCorrectingVel(world, f); +} + +void World::SetContactSurfaceLayer(float f){ + dWorldSetContactSurfaceLayer(world, f); +} + +void World::SetGravity(const vec2 &v){ + dWorldSetGravity(world, v.x, v.y, 0); +} + +dJointID World::AddMotor(Object &object){ + dJointID joint = dJointCreateAMotor(world, 0); + dJointAttach(joint, object.GetBody(), 0); + dJointSetAMotorNumAxes(joint, 1); + dJointSetAMotorAxis(joint, 0, 1, 0, 0, 1); + dJointSetAMotorParam(joint, dParamFMax, dInfinity); + return joint; +} + +dJointID World::Glue(Object &object1, Object &object2){ + dJointID joint = dJointCreateFixed(world, 0); + dJointAttach(joint, object1.GetBody(), object2.GetBody()); + dJointSetFixed(joint); + return joint; +} + +dJointID World::AnchorAxis(Object &object, const vec2 &axis){ + dJointID joint = dJointCreateSlider(world, 0); + dJointAttach(joint, object.GetBody(), 0); + dJointSetSliderAxis(joint, axis.x, axis.y, 0); + return joint; +} + +dJointID World::Anchor(Object &o1, Object &o2, const vec2 &point, float mu, float erp){ + dJointID joint = dJointCreateHinge(world, 0); + dJointAttach(joint, o1.GetBody(), o2.GetBody()); + dJointSetHingeAnchor(joint, point.x, point.y, 0); + dJointSetHingeAxis(joint, 0, 0, 1); + dJointSetErp(joint, erp); + + if (mu){ + dJointID friction = dJointCreateAMotor(world, 0); + dJointAttach(friction, o1.GetBody(), o2.GetBody()); + + dJointSetAMotorNumAxes(friction, 1); + dJointSetAMotorAxis(friction, 0, 1, 0, 0, 1); + dJointSetAMotorParam(friction, dParamFMax, mu); + dJointSetAMotorParam(friction, dParamVel, 0); + } + return joint; +} + +dJointID World::Anchor(Object &o1, const vec2 &point, float mu, float erp){ + dJointID joint = dJointCreateHinge(world, 0); + dJointAttach(joint, o1.GetBody(), 0); + dJointSetHingeAnchor(joint, point.x, point.y, 0); + dJointSetHingeAxis(joint, 0, 0, 1); + dJointSetErp(joint, erp); + + if (mu){ + dJointID friction = dJointCreateAMotor(world, 0); + dJointAttach(friction, o1.GetBody(), 0); + + dJointSetAMotorNumAxes(friction, 1); + dJointSetAMotorAxis(friction, 0, 1, 0, 0, 1); + dJointSetAMotorParam(friction, dParamFMax, mu); + dJointSetAMotorParam(friction, dParamVel, 0); + } + return joint; +} + +void World::DeleteJoint(dJointID joint){ + dJointDestroy(joint); +} + +void World::SetMotorVelocity(dJointID joint, float velocity){ + dJointSetAMotorParam(joint, dParamVel, velocity); +} + +float World::GetMotorVelocity(dJointID joint){ + return dJointGetAMotorParam(joint, dParamVel); +} + +ContactList::ContactList(){ + for (int i = 0; i < Max; ++i){ + contacts[i].geom.pos[2] = 0; + contacts[i].geom.normal[2] = 0; + } + Reset(0, 0); +} + +void ContactList::Reset(Object *o1, Object *o2){ + count = 0; + invertNormals = false; + this->o1 = o1; + this->o2 = o2; +} + +void ContactList::AddContact(const vec2 &position, const vec2 &normal, float depth){ + assert(count < Max - 1); + dContactGeom &cg = contacts[count].geom; + cg.pos[0] = position.x; + cg.pos[1] = position.y; + if (invertNormals){ + cg.normal[0] = -normal.x; + cg.normal[1] = -normal.y; + } + else{ + cg.normal[0] = normal.x; + cg.normal[1] = normal.y; + } + cg.depth = depth; + ++count; +} + +void ContactList::Finalize(){ + ObjectProperties &p1 = o1->Property(); + ObjectProperties &p2 = o2->Property(); + + for (int i = 0; i < count; ++i){ + dContact &c = contacts[i]; + c.surface.mode = dContactBounce; + + if (p1.frictionMask & p2.frictionMask){ + if (p1.friction == dInfinity || p2.friction == dInfinity) + c.surface.mu = dInfinity; + else + c.surface.mu = p1.friction * p2.friction; + } + else + c.surface.mu = 0; + + c.surface.bounce = (p1.bounceFactor + p2.bounceFactor) / 2; + c.surface.bounce_vel = (p1.bounceVelocity + p2.bounceVelocity) / 2; + + dContactGeom &cg = contacts[i].geom; + cg.g1 = o1; + cg.g2 = o2; + } + + if (p1.callback) p1.callback(*this); + + if (p2.callback){ + std::swap(o1, o2); + p2.callback(*this); + std::swap(o1, o2); + } +} + +void ContactList::CreateJoints(dWorldID world, dJointGroupID contactGroup) const{ + for (int i = 0; i < count; ++i){ + const dContact &c = contacts[i]; + dJointID joint = dJointCreateContact(world, contactGroup, &c); + + const dContactGeom &cg = c.geom; + dJointAttach(joint, cg.g1->GetBody(), cg.g2->GetBody()); + } +} diff --git a/HenocUniverse/source/intersection.cpp b/HenocUniverse/source/intersection.cpp new file mode 100755 index 0000000..106a4a3 --- /dev/null +++ b/HenocUniverse/source/intersection.cpp @@ -0,0 +1,41 @@ +// Summary: Calls appropriate Test and Find methods for all shape-shape combinations. +/* + * TT102 HENOC + * 2008 @ ESCOM-IPN + * metodos de busqueda de intersecciones de todos los tipos figura-figura + */ + +#include +#include +#include + +bool HenocUniverse::Intersection::Test(const Geometry &g1, const Geometry &g2){ + if (!g1.GetBounds().intersects(g2.GetBounds())) + return false; + + typedef bool TestFunction(const Geometry &g1, const Geometry &g2); + static TestFunction *table[Shape::Count][Shape::Count] ={ + TestQuadQuad, TestQuadQuad, TestQuadCircle, TestQuadTerrain, TestQuadComposite, + TestQuadQuad, TestQuadQuad, TestQuadCircle, TestQuadTerrain, TestQuadComposite, + TestCircleQuad, TestCircleQuad, TestCircleCircle, TestCircleTerrain, TestCircleComposite, + TestTerrainQuad, TestTerrainQuad, TestTerrainCircle, TestTerrainTerrain, TestTerrainComposite, + TestCompositeQuad, TestCompositeQuad, TestCompositeCircle, TestCompositeTerrain, TestCompositeComposite + }; + + return table[g1.GetShape()][g2.GetShape()](g1, g2); +} + +void HenocUniverse::Intersection::Find(const Geometry &g1, const Geometry &g2, ContactList &contacts){ + if (!g1.GetBounds().intersects(g2.GetBounds())) return; + + typedef void FindFunction(const Geometry &g1, const Geometry &g2, ContactList &contacts); + static FindFunction *table[Shape::Count][Shape::Count] ={ + FindQuadQuad, FindQuadQuad, FindQuadCircle, FindQuadTerrain, FindQuadComposite, + FindQuadQuad, FindQuadQuad, FindQuadCircle, FindQuadTerrain, FindQuadComposite, + FindCircleQuad, FindCircleQuad, FindCircleCircle, FindCircleTerrain, FindCircleComposite, + FindTerrainQuad, FindTerrainQuad, FindTerrainCircle, FindTerrainTerrain, FindTerrainComposite, + FindCompositeQuad, FindCompositeQuad, FindCompositeCircle, FindCompositeTerrain, FindCompositeComposite + }; + + table[g1.GetShape()][g2.GetShape()](g1, g2, contacts); +} diff --git a/HenocUniverse/source/quad-circle.cpp b/HenocUniverse/source/quad-circle.cpp new file mode 100755 index 0000000..040dc7f --- /dev/null +++ b/HenocUniverse/source/quad-circle.cpp @@ -0,0 +1,102 @@ +/* + * TT102 HENOC + * 2008 @ ESCOM-IPN + * Intersecciones cuadrado-circulo y generador de contactos + */ + +#include +#include + + +bool HenocUniverse::Intersection::TestCircleQuad(const Geometry &c, const Geometry &q){ + return TestQuadCircle(q, c); +} + +bool HenocUniverse::Intersection::TestQuadCircle(const Geometry &g1, const Geometry &g2){ + assert(g1.GetShape() == Shape::Quad); + assert(g2.GetShape() == Shape::Circle); + const Quad &q = static_cast(g1); + const Circle &c = static_cast(g2); + + bool onborder = false; + vec2 l, t; + + vec2 p = c.Center() - q.Center(); + l.x = q.Extent(0); + t.x = dot(p, q.Axis(0)); + if (t.x < -l.x) { t.x = -l.x; onborder = true; } + if (t.x > l.x) { t.x = l.x; onborder = true; } + + l.y = q.Extent(1); + t.y = dot(p, q.Axis(1)); + if (t.y < -l.y) { t.y = -l.y; onborder = true; } + if (t.y > l.y) { t.y = l.y; onborder = true; } + + if (!onborder) return true; + + vec2 qq, r; + vec2 a(q.Axis(0).x, q.Axis(1).x); + vec2 b(q.Axis(0).y, q.Axis(1).y); + qq.x = dot(t, a); + qq.y = dot(t, b); + r = p - qq; + float depth = c.Radius() - r.length(); + return depth >= 0; +} + +void HenocUniverse::Intersection::FindCircleQuad(const Geometry &c, const Geometry &q, ContactList &contacts){ + contacts.ToggleNormalInversion(); + FindQuadCircle(q, c, contacts); + contacts.ToggleNormalInversion(); +} + +void HenocUniverse::Intersection::FindQuadCircle(const Geometry &g1, const Geometry &g2, ContactList &contacts){ + assert(g1.GetShape() == Shape::Quad); + assert(g2.GetShape() == Shape::Circle); + const Quad &q = static_cast(g1); + const Circle &c = static_cast(g2); + + bool onborder = false; + vec2 l, t; + + vec2 p = c.Center() - q.Center(); + l.x = q.Extent(0); + t.x = dot(p, q.Axis(0)); + if (t.x < -l.x) { t.x = -l.x; onborder = true; } + if (t.x > l.x) { t.x = l.x; onborder = true; } + + l.y = q.Extent(1); + t.y = dot(p, q.Axis(1)); + if (t.y < -l.y) { t.y = -l.y; onborder = true; } + if (t.y > l.y) { t.y = l.y; onborder = true; } + + if (!onborder){ + float min_distance = l.x - fabsf(t.x); + vec2 normal = t.x < 0 ? -q.Axis(0) : q.Axis(0); + + float face_distance = l.y - fabsf(t.y); + if (face_distance < min_distance){ + min_distance = face_distance; + normal = t.y < 0 ? q.Axis(1) : -q.Axis(1); + } + + float depth = min_distance + c.Radius(); + contacts.AddContact(c.Center(), normal, depth); + } + + vec2 qq, r; + vec2 a(q.Axis(0).x, q.Axis(1).x); + vec2 b(q.Axis(0).y, q.Axis(1).y); + qq.x = dot(t, a); + qq.y = dot(t, b); + r = p - qq; + + float distance = r.length(); + float depth = c.Radius() - distance; + if (depth < 0) return; + + if (distance) r.normalize(); + else r = vec2(1, 0); + + contacts.AddContact(qq + q.Center(), -r, depth); +} diff --git a/HenocUniverse/source/quad-quad.cpp b/HenocUniverse/source/quad-quad.cpp new file mode 100755 index 0000000..8af25d8 --- /dev/null +++ b/HenocUniverse/source/quad-quad.cpp @@ -0,0 +1,300 @@ +/* + * TT102 HENOC + * 2008 @ ESCOM-IPN + * Intersecciones cuadrado-cuadrado y generador de contactos + */ + +#include +#include + + + +namespace{ + HenocUniverse::vec2 delta; + float dotA0B0, dotA0B1, dotA1B0, dotA1B1; + float absDotA0B0, absDotA0B1, absDotA1B0, absDotA1B1; + float extAA0B0, extAA0B1, extAA1B0, extAA1B1, extBA0B0, extBA0B1, extBA1B0, extBA1B1; + float depth; + enum ECode {NA0, NA1, NB0, NB1, PA0, PA1, PB0, PB1} code; +} + +bool HenocUniverse::Intersection::TestQuadQuad(const Geometry &g1, const Geometry &g2){ + assert(g1.GetShape() == Shape::Quad); + assert(g2.GetShape() == Shape::Quad); + const Quad &a = static_cast(g1); + const Quad &b = static_cast(g2); + + delta = a.Center() - b.Center(); + float minDepth = HUGE_VAL; + + // test the seperating axis of a.Axis(0) + dotA0B0 = dot(a.Axis(0), b.Axis(0)); + absDotA0B0 = fabsf(dotA0B0); + dotA0B1 = dot(a.Axis(0), b.Axis(1)); + absDotA0B1 = fabsf(dotA0B1); + extBA0B0 = b.Extent(0)*absDotA0B0; + extBA0B1 = b.Extent(1)*absDotA0B1; + float dist1 = dot(a.Axis(0), delta); + float dist2 = a.Extent(0) + extBA0B0 + extBA0B1; + depth = dist2 - fabsf(dist1); + if (depth < 0) + return false; + if (depth < minDepth) + { + minDepth = depth; + code = dist1 < 0 ? NA0 : PA0; + } + + // test the seperating axis of a.Axis(1) + dotA1B0 = dot(a.Axis(1), b.Axis(0)); + absDotA1B0 = fabsf(dotA1B0); + dotA1B1 = dot(a.Axis(1), b.Axis(1)); + absDotA1B1 = fabsf(dotA1B1); + extBA1B0 = b.Extent(0)*absDotA1B0; + extBA1B1 = b.Extent(1)*absDotA1B1; + dist1 = dot(a.Axis(1), delta); + dist2 = a.Extent(1) + extBA1B0 + extBA1B1; + depth = dist2 - fabsf(dist1); + if (depth < 0) + return false; + if (depth < minDepth) + { + minDepth = depth; + code = dist1 < 0 ? NA1 : PA1; + } + + // test the seperating axis of b.Axis(0) + extAA0B0 = a.Extent(0)*absDotA0B0; + extAA1B0 = a.Extent(1)*absDotA1B0; + dist1 = dot(b.Axis(0), delta); + dist2 = b.Extent(0) + extAA0B0 + extAA1B0; + depth = dist2 - fabsf(dist1); + if (depth < 0) + return false; + if (depth < minDepth) + { + minDepth = depth; + code = dist1 < 0 ? NB0 : PB0; + } + + // test the seperating axis of b.Axis(1) + extAA0B1 = a.Extent(0)*absDotA0B1; + extAA1B1 = a.Extent(1)*absDotA1B1; + dist1 = dot(b.Axis(1), delta); + dist2 = b.Extent(1) + extAA0B1 + extAA1B1; + depth = dist2 - fabsf(dist1); + if (depth < 0) + return false; + if (depth < minDepth) + { + minDepth = depth; + code = dist1 < 0 ? NB1 : PB1; + } + + return true; +} + +void HenocUniverse::Intersection::FindQuadQuad(const Geometry &g1, const Geometry &g2, ContactList &contacts){ + assert(g1.GetShape() == Shape::Quad); + assert(g2.GetShape() == Shape::Quad); + const Quad &a = static_cast(g1); + const Quad &b = static_cast(g2); + + vec2 normal; + vec2 normal2; + vec2 center; + int axis0, axis1; + int a0, a1; + const Quad *reference; + const Quad *incident; + float m11; + float k1; + bool negate; + + switch (code){ + case PA0: + normal = a.Axis(0); + axis0 = 0; axis1 = 1; + normal2 = -normal; + reference = &a; incident = &b; + a0 = absDotA0B1 > absDotA0B0 ? 1 : 0; a1 = 1 - a0; + if (a1){ + m11 = dotA1B1; + k1 = extBA1B1; + negate = -dotA0B0 < 0; + } + else{ + m11 = dotA1B0; + k1 = extBA1B0; + negate = -dotA0B1 < 0; + } + center = -delta; + break; + + case PA1: + normal = a.Axis(1); + axis0 = 1; axis1 = 0; + normal2 = -normal; + reference = &a; incident = &b; + a0 = absDotA1B1 > absDotA1B0 ? 1 : 0; a1 = 1 - a0; + if (a1){ + m11 = dotA0B1; + k1 = extBA0B1; + negate = -dotA1B0 < 0; + } + else{ + m11 = dotA0B0; + k1 = extBA0B0; + negate = -dotA1B1 < 0; + } + center = -delta; + break; + + case PB0: + normal = b.Axis(0); + axis0 = 0; axis1 = 1; + normal2 = normal; + reference = &b; incident = &a; + a0 = absDotA1B0 > absDotA0B0 ? 1 : 0; a1 = 1 - a0; + if (a1){ + m11 = dotA1B1; + k1 = extAA1B1; + negate = dotA0B0 < 0; + } + else{ + m11 = dotA0B1; + k1 = extAA0B1; + negate = dotA1B0 < 0; + } + center = delta; + break; + + case PB1: + normal = b.Axis(1); + axis0 = 1; axis1 = 0; + normal2 = normal; + reference = &b; incident = &a; + a0 = absDotA1B1 > absDotA0B1 ? 1 : 0; a1 = 1 - a0; + if (a1){ + m11 = dotA1B0; + k1 = extAA1B0; + negate = dotA0B1 < 0; + } + else{ + m11 = dotA0B0; + k1 = extAA0B0; + negate = dotA1B1 < 0; + } + center = delta; + break; + + case NA0: + normal = -a.Axis(0); + axis0 = 0; axis1 = 1; + normal2 = -normal; + reference = &a; incident = &b; + a0 = absDotA0B1 > absDotA0B0 ? 1 : 0; a1 = 1 - a0; + if (a1){ + m11 = dotA1B1; + k1 = extBA1B1; + negate = dotA0B0 < 0; + } + else{ + m11 = dotA1B0; + k1 = extBA1B0; + negate = dotA0B1 < 0; + } + center = -delta; + break; + + case NA1: + normal = -a.Axis(1); + axis0 = 1; axis1 = 0; + normal2 = -normal; + reference = &a; incident = &b; + a0 = absDotA1B1 > absDotA1B0 ? 1 : 0; a1 = 1 - a0; + if (a1){ + m11 = dotA0B1; + k1 = extBA0B1; + negate = dotA1B0 < 0; + } + else{ + m11 = dotA0B0; + k1 = extBA0B0; + negate = dotA1B1 < 0; + } + center = -delta; + break; + + case NB0: + normal = -b.Axis(0); + axis0 = 0; axis1 = 1; + normal2 = normal; + reference = &b; incident = &a; + a0 = absDotA1B0 > absDotA0B0 ? 1 : 0; a1 = 1 - a0; + if (a1){ + m11 = dotA1B1; + k1 = extAA1B1; + negate = -dotA0B0 < 0; + } + else{ + m11 = dotA0B1; + k1 = extAA0B1; + negate = -dotA1B0 < 0; + } + center = delta; + break; + + case NB1: + normal = -b.Axis(1); + axis0 = 1; axis1 = 0; + normal2 = normal; + reference = &b; incident = &a; + a0 = absDotA1B1 > absDotA0B1 ? 1 : 0; a1 = 1 - a0; + if (a1){ + m11 = dotA1B0; + k1 = extAA1B0; + negate = -dotA0B1 < 0; + } + else{ + m11 = dotA0B0; + k1 = extAA0B0; + negate = -dotA1B1 < 0; + } + center = delta; + break; + } + + if (negate) + center += incident->EAxis(a0); + else + center -= incident->EAxis(a0); + + float collisions[2]; + int count = 0; + { + float c1 = dot(center, reference->Axis(axis1)); + float a = c1 - k1; + float b = c1 + k1; + float w = reference->Extent(axis1); + + if (w > a && w < b) + collisions[count++] = w - c1; + if (a < w && a > -w) + collisions[count++] = -k1; + if (-w > a && -w < b) + collisions[count++] = -w - c1; + if (b < w && b > -w) + collisions[count++] = k1; + } + + assert(count < 3); + + for (int j = 0; j < count; ++j){ + float k1 = collisions[j] / m11; + vec2 point = center + incident->Axis(a1) * k1; + float depth = reference->Extent(axis0) - dot(normal2, point); + if (depth >= 0) + contacts.AddContact(point + reference->Center(), normal, depth); + } +} diff --git a/HenocUniverse/source/shapes.cpp b/HenocUniverse/source/shapes.cpp new file mode 100755 index 0000000..94431b4 --- /dev/null +++ b/HenocUniverse/source/shapes.cpp @@ -0,0 +1,206 @@ +/* + * TT102 HENOC + * 2008 @ ESCOM-IPN + * Define metodos para Quad, Wall, Block, Circle, Line, Terrain. + */ + +#include +#include +#include +#include +#include + +using namespace HenocUniverse; + +Quad::Quad(float width, float height){ + extent[0] = width / 2; + extent[1] = height / 2; + UpdateBounds(); +} + +Block::Block(vec2 topleft, vec2 bottomright) : Quad(bottomright.x - topleft.x, bottomright.y - topleft.y){ + SetCenter(vec2(topleft.x + bottomright.x, topleft.y + bottomright.y) / 2); + SetAxis(vec2(1, 0)); + UpdateBounds(); +} + +Block::Block(vec2 center, float width, float height) : Quad(width, height){ + SetCenter(center); + SetAxis(vec2(1, 0)); + UpdateBounds(); +} + +Block::Block(float left, float top, float right, float bottom) : Quad(right - left, bottom - top){ + SetCenter(vec2(right + left, top + bottom) / 2); + SetAxis(vec2(1, 0)); + UpdateBounds(); +} + +Line::Line(vec2 a, vec2 b) : Quad((b - a).length(), 0){ + SetCenter((a + b) / 2); + SetAxis((b - a).hat()); + UpdateBounds(); +} + +Terrain::Terrain(const vec2 &start) : previous(start){ + vertices.push_back(start); + spans[start.x] = 0; +} + +Circle::Circle(const vec2 ¢er, float radius) : radius(radius){ + SetCenter(center); + UpdateBounds(); +} + +void Quad::UpdateBounds(){ + vec2 eaxis[2] = {EAxis(0), EAxis(1)}; + + corners[0] = center - eaxis[0] - eaxis[1]; + corners[1] = center + eaxis[0] - eaxis[1]; + corners[2] = center + eaxis[0] + eaxis[1]; + corners[3] = center - eaxis[0] + eaxis[1]; + + bounds.left = std::min(std::min(std::min(corners[0].x, corners[1].x), corners[2].x), corners[3].x); + bounds.top = std::min(std::min(std::min(corners[0].y, corners[1].y), corners[2].y), corners[3].y); + bounds.right = std::max(std::max(std::max(corners[0].x, corners[1].x), corners[2].x), corners[3].x); + bounds.bottom = std::max(std::max(std::max(corners[0].y, corners[1].y), corners[2].y), corners[3].y); +} + +bool Quad::Contains(const vec2 &v) const{ + vec2 p = v - center; + p = p.rotate(axis); + return fabsf(p.x) < extent[0] && fabsf(p.y) < extent[1]; +} + +void Terrain::push_back(const vec2 &v){ + assert(v.x > previous.x && "terrains must be built in left-to-right order"); + lines.push_back(Line(previous, v)); + spans[v.x] = lines.size(); + previous = v; + vertices.push_back(v); +} + +bool Terrain::GetIndexRange(float left, float right, int &lower, int &upper) const{ + SpanMap::const_iterator lowerBound = spans.lower_bound(left); + if (lowerBound == spans.end()) return false; + + lower = lowerBound->second - 1; + if (lower < 0) lower = 0; + assert(lower < (int) lines.size()); + + SpanMap::const_iterator upperBound = spans.upper_bound(right); + if (upperBound == spans.end()) + upper = lines.size() - 1; + else + upper = upperBound->second - 1; + assert(upper >= 0 && upper < (int) lines.size()); + return true; +} + +void Terrain::UpdateBounds(){ + bounds.left = HUGE_VAL; + bounds.top = HUGE_VAL; + bounds.right = -HUGE_VAL; + bounds.bottom = -HUGE_VAL; + if (lines.empty()) return; + + const Line &front = lines.front(); + bounds.left = std::min(bounds.left, front.Origin().x); + + for (LineStrip::const_iterator l = lines.begin(); l != lines.end(); ++l){ + bounds.top = std::min(bounds.top, l->Origin().y); + bounds.bottom = std::max(bounds.bottom, l->Origin().y); + } + + const Line &back = lines.back(); + bounds.top = std::min(bounds.top, back.End().y); + bounds.bottom = std::max(bounds.bottom, back.End().y); + bounds.right = std::max(bounds.right, back.End().x); +} + +void Circle::UpdateBounds(){ + bounds.left = center.x - radius; + bounds.top = center.y - radius; + bounds.right = center.x + radius; + bounds.bottom = center.y + radius; +} + + +Composite::Composite(vec2 centroid){ + SetCenter(centroid); + UpdateBounds(); +} + +Composite::~Composite() { + for (iterator g = geometries.begin(); g != geometries.end(); ++g) + delete &(*g); +} + +void Composite::UpdateBounds(){ + bounds.left = HUGE_VAL; + bounds.top = HUGE_VAL; + bounds.right = -HUGE_VAL; + bounds.bottom = -HUGE_VAL; + if (geometries.empty()) return; + + for (iterator g = geometries.begin(); g != geometries.end(); ++g){ + (*g)->UpdateBounds(); + const aabb &b = (*g)->GetBounds(); + if (b.left < bounds.left) bounds.left = b.left; + if (b.right > bounds.right) bounds.right = b.right; + if (b.top < bounds.top) bounds.top = b.top; + if (b.bottom > bounds.bottom) bounds.bottom = b.bottom; + } +} + +void Composite::push_back(Geometry *geometry){ + geometries.push_back(geometry); +} + +void Composite::SetCenter(const vec2 ¢er){ + vec2 delta = center - this->center; + Geometry::SetCenter(center); + for (const_iterator g = begin(); g != end(); ++g) + (*g)->SetCenter((*g)->Center() + delta); +} + +void Composite::SetAxis(const vec2 &axis){ + vec2 rotation = this->axis.rotate(axis); + + for (const_iterator g = begin(); g != end(); ++g){ + vec2 oldAxis = (*g)->Axis(); + vec2 oldCenter = (*g)->Center(); + + vec2 newAxis = oldAxis.rotate(rotation); + (*g)->SetAxis(newAxis); + + vec2 delta = oldCenter - center; + if (!delta.x && !delta.y) continue; + + vec2 newCenter = center + delta.rotate(rotation); + (*g)->SetCenter(newCenter); + } + Geometry::SetAxis(axis); +} + +void Quad::SetMass(Body body, float density) const{ + dMass m; + if (extent[1]) + dMassSetBox(&m, density, 2 * extent[0], 2 * extent[1], 1); + else + dMassSetBox(&m, density, 2 * extent[0], 1, 1); + dBodySetMass(body, &m); +} + +void Circle::SetMass(Body body, float density) const{ + dMass m; + dMassSetBox(&m, density, radius, radius, 1); // TODO + dBodySetMass(body, &m); +} + +void Composite::SetMass(Body body, float density) const{ + dMass m; + dMassSetBox(&m, density, 100, 100, 1); // TODO + dBodySetMass(body, &m); +} + diff --git a/HenocUniverse/source/terrain-all.cpp b/HenocUniverse/source/terrain-all.cpp new file mode 100755 index 0000000..abe4710 --- /dev/null +++ b/HenocUniverse/source/terrain-all.cpp @@ -0,0 +1,89 @@ +/* + * TT102 HENOC + * 2008 @ ESCOM-IPN + * Intersecciones circulo-circulo y generador de contactos + */ + +#include +#include + +namespace{ + int lower, upper; +} + +using namespace HenocUniverse; + +bool TestTerrainGeometry(const Geometry &g1, const Geometry &g2){ + assert(g1.GetShape() == Shape::Terrain); + const Terrain &t = static_cast(g1); + const Geometry &g = g2; + + if (!t.GetIndexRange(g.GetBounds().left, g.GetBounds().right, lower, upper)) + return false; + + for (int index = lower; index <= upper; ++index){ + assert(index >= 0 && index < (int) t.size()); + const Line &line = t[index]; + if (Intersection::Test(line, g)) return true; + } + + return false; +} + +void FindTerrainGeometry(const Geometry &g1, const Geometry &g2, ContactList &contacts){ + assert(g1.GetShape() == Shape::Terrain); + const Terrain &t = static_cast(g1); + const Geometry &g = g2; + + for (int index = lower; index <= upper; ++index){ + const Line &line = t[index]; + if (Intersection::Test(line, g)) + Intersection::Find(line, g, contacts); + } +} + +void Intersection::FindTerrainQuad(const Geometry &g1, const Geometry &g2, ContactList &contacts){ + FindTerrainGeometry(g1, g2, contacts); +} + +void Intersection::FindTerrainCircle(const Geometry &g1, const Geometry &g2, ContactList &contacts){ + FindTerrainGeometry(g1, g2, contacts); +} + +void Intersection::FindTerrainTerrain(const Geometry &g1, const Geometry &g2, ContactList &contacts){ + FindTerrainGeometry(g1, g2, contacts); +} + +void Intersection::FindQuadTerrain(const Geometry &g1, const Geometry &g2, ContactList &contacts){ + contacts.ToggleNormalInversion(); + FindTerrainGeometry(g2, g1, contacts); + contacts.ToggleNormalInversion(); +} + +void Intersection::FindCircleTerrain(const Geometry &g1, const Geometry &g2, ContactList &contacts){ + contacts.ToggleNormalInversion(); + FindTerrainGeometry(g2, g1, contacts); + contacts.ToggleNormalInversion(); +} + +bool Intersection::TestTerrainQuad(const Geometry &g1, const Geometry &g2){ + return TestTerrainGeometry(g1, g2); +} + +bool Intersection::TestTerrainCircle(const Geometry &g1, const Geometry &g2){ + return TestTerrainGeometry(g1, g2); +} + +bool Intersection::TestTerrainTerrain(const Geometry &g1, const Geometry &g2){ + return TestTerrainGeometry(g1, g2); +} + +bool Intersection::TestQuadTerrain(const Geometry &g1, const Geometry &g2){ + return TestTerrainGeometry(g2, g1); +} + +bool Intersection::TestCircleTerrain(const Geometry &g1, const Geometry &g2){ + return TestTerrainGeometry(g2, g1); +} + + diff --git a/HenocUniverse/source/vector.cpp b/HenocUniverse/source/vector.cpp new file mode 100755 index 0000000..647160a --- /dev/null +++ b/HenocUniverse/source/vector.cpp @@ -0,0 +1,39 @@ +/* + * TT102 HENOC + * 2008 @ ESCOM-IPN + * Define vec2 + */ + +#include +#include + +using namespace HenocUniverse; + +const float d2r = pi / 180; + +vec2 vec2::rotate(float d) const { return rotate(vec2(cosf(d2r * d), sinf(d2r * d))); } +float vec2::length() const { return sqrtf(dot(*this, *this)); } + +bool HenocUniverse::is_nan(float f){ + unsigned long bits = *((unsigned long*) &f); + + if (bits >= 0xffc00001 && bits <= 0xffffffff) return true; + + if (bits == 0xffc00000) return true; + + if (bits >= 0xff800001 && bits <= 0xffbfffff) return true; + + if (bits == 0xff800000) return true; + + if (bits == 0x7f800000) return true; + + if (bits >= 0x7f800001 && bits <= 0x7fbfffff) return true; + + if (bits >= 0x7fc00000 && bits <= 0x7fffffff) return true; + + return false; +} + +float HenocUniverse::round(float f){ + return (float) ((int) (f + 0.5f)); +} diff --git a/HenocUniverse/vector.h b/HenocUniverse/vector.h new file mode 100755 index 0000000..b4cfc68 --- /dev/null +++ b/HenocUniverse/vector.h @@ -0,0 +1,46 @@ +/* + * TT102 HENOC + * 2008 @ ESCOM-IPN + * + */ +#ifndef _HENOC_VECTOR_H +#define _HENOC_VECTOR_H + +namespace HenocUniverse{ + const float pi = 3.1415926535897932384626433832795f; + bool is_nan(float f); + float round(float f); + + struct vec2; + float dot(const vec2 &a, const vec2 &b); + float cross(vec2 a, vec2 b); + + struct vec2{ + vec2() {} + vec2(float x, float y) : x(x), y(y) {} + vec2(const float *f) : x(f[0]), y(f[1]) {} + float length() const; + vec2 rotate(float d) const; + float squared_length() const { return dot(*this, *this); } + void normalize() { *this /= length(); } + vec2 rotate(vec2 v) const { return vec2(x * v.x + y * v.y, v.x * y - x * v.y); } + vec2 hat() const { return *this / length(); } + vec2 perp() const { return vec2(-y, x); } + vec2 flip() const { return vec2(y, x); } + void snap(float epsilon); + vec2 operator-() const { return vec2(-x, -y); } + vec2 &operator-=(vec2 v) { x -= v.x; y -= v.y; return *this; } + vec2 &operator+=(vec2 v) { x += v.x; y += v.y; return *this; } + vec2 &operator/=(float f) { x /= f; y /= f; return *this; } + vec2 operator+(vec2 v) const { return vec2(x + v.x, y + v.y); } + vec2 operator-(vec2 v) const { return vec2(x - v.x, y - v.y); } + vec2 operator*(float f) const { return vec2(x * f, y * f); } + vec2 operator/(float f) const { return vec2(x / f, y / f); } + float x, y; + }; + + inline float dot(const vec2 &a, const vec2 &b) { return a.x * b.x + a.y * b.y; } + inline float cross(vec2 a, vec2 b) { return a.x * b.y - a.y * b.x; } +} + +#endif diff --git a/autoclean.sh b/autoclean.sh new file mode 100644 index 0000000..84df20e --- /dev/null +++ b/autoclean.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +######## +## +## Archivo que compila las librerias y el proyecto completo, ademas de eliminar +## residuos que puedan interferir con la generacion de librerias y binarios +## +######## + +case $TERM in + xterm|rxvt|Eterm|eterm|linux) + ECHO_COMMAND='echo -ne' + OS="linux" + ;; + xterm-color) + ECHO_COMMAND='echo' + OS="osx" + ;; + cygwin) + ECHO_COMMAND='echo -ne' + OS="windows" +esac + + +${ECHO_COMMAND} "\n\n\033[1m**********************************************************\033[0m \n" +${ECHO_COMMAND} "\033[1m Limpiando objetos y binarios anteriores... \033[0m \n" +${ECHO_COMMAND} "\033[1m**********************************************************\033[0m \n\n" + +rm -fv Henoc.exe && rm -rfv Henoc.app && rm -fv Henoc +cd HenocUniverse/ && make clean +cd - +cd FullHenoc/ && qmake Henoc.pro && make clean && rm -fv Makefile +cd - diff --git a/autohenoc.sh b/autohenoc.sh new file mode 100644 index 0000000..c24138f --- /dev/null +++ b/autohenoc.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +######## +## +## Archivo que compila las librerias y el proyecto completo, ademas de eliminar +## residuos que puedan interferir con la generacion de librerias y binarios +## +######## + +case $TERM in + xterm|rxvt|Eterm|eterm|linux) + ECHO_COMMAND='echo -ne' + OS="linux" + ;; + xterm-color) + ECHO_COMMAND='echo' + OS="osx" + ;; + cygwin) + ECHO_COMMAND='echo -ne' + OS="windows" +esac + + +${ECHO_COMMAND} "\n\n\033[1m**********************************************************\033[0m \n" +${ECHO_COMMAND} "\033[1m Limpiando objetos y binarios anteriores... \033[0m \n" +${ECHO_COMMAND} "\033[1m**********************************************************\033[0m \n\n" + +rm -fv Henoc.exe && rm -rfv Henoc.app && rm -fv Henoc +cd HenocUniverse/ && make clean +cd - +cd FullHenoc/ && qmake Henoc.pro && make clean && rm -fv Makefile +cd - + +${ECHO_COMMAND} "\n\n\033[1m**********************************************************\033[0m \n" +${ECHO_COMMAND} "\033[1m Compilando libreria... \033[0m \n" +${ECHO_COMMAND} "\033[1m**********************************************************\033[0m \n\n" +cd HenocUniverse/ +make +cd - + +${ECHO_COMMAND} "\n\n\033[1m**********************************************************\033[0m \n" +${ECHO_COMMAND} "\033[1m Generando Makefile del proyecto... \033[0m \n" +${ECHO_COMMAND} "\033[1m**********************************************************\033[0m \n\n" +cd FullHenoc/ +qmake Henoc.pro && echo "OK..." + +${ECHO_COMMAND} "\n\n\033[1m**********************************************************\033[0m \n" +${ECHO_COMMAND} "\033[1m Compilando proyecto... \033[0m \n" +${ECHO_COMMAND} "\033[1m**********************************************************\033[0m \n\n" +make +cd -